mirror of https://github.com/OpenRCT2/OpenRCT2.git
Merge branch 'develop'
This commit is contained in:
commit
87fe784da0
|
@ -156,7 +156,7 @@ jobs:
|
|||
name: Windows (${{ matrix.platform_name }}) using mingw
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-code-formatting
|
||||
container: openrct2/openrct2-build:13-mingw
|
||||
container: openrct2/openrct2-build:14-mingw
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
@ -293,7 +293,7 @@ jobs:
|
|||
echo 'Not going to push build'
|
||||
fi
|
||||
linux-portable:
|
||||
name: Linux (${{ matrix.platform }}, ${{ matrix.distro }}, portable)
|
||||
name: ${{ matrix.distro }} Linux (${{ matrix.release }}, ${{ matrix.platform }}, portable)
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-code-formatting
|
||||
container: ${{ matrix.image }}
|
||||
|
@ -302,23 +302,21 @@ jobs:
|
|||
matrix:
|
||||
include:
|
||||
# Use `-fno-var-tracking-assignments` to reduce amount of produced debug information. This is necessary due to 100MiB limit of GitHub / openrct2.org API.
|
||||
# For focal the debug information still takes too much space, so reduce amount of debug info to minimum using `-g1` (`-g` means `-g2`), which is enough for backtraces only.
|
||||
- platform: x86_64
|
||||
distro: focal
|
||||
image: openrct2/openrct2-build:13-focal
|
||||
build_flags: -DCMAKE_POSITION_INDEPENDENT_CODE=on -DCMAKE_CXX_FLAGS="-g1 -gz" -DWITH_TESTS=off
|
||||
- platform: x86_64
|
||||
distro: jammy
|
||||
image: openrct2/openrct2-build:13-jammy
|
||||
distro: Ubuntu
|
||||
release: jammy
|
||||
image: openrct2/openrct2-build:14-jammy
|
||||
build_flags: -DCMAKE_POSITION_INDEPENDENT_CODE=on -DCMAKE_CXX_FLAGS="-g -gz -fno-var-tracking-assignments"
|
||||
- platform: x86_64
|
||||
distro: bullseye
|
||||
image: openrct2/openrct2-build:13-bullseye
|
||||
distro: Debian
|
||||
release: bullseye
|
||||
image: openrct2/openrct2-build:14-bullseye
|
||||
build_flags: -DCMAKE_POSITION_INDEPENDENT_CODE=on -DCMAKE_CXX_FLAGS="-g -gz -fno-var-tracking-assignments" -DWITH_TESTS=off
|
||||
- platform: i686
|
||||
distro: focal
|
||||
image: openrct2/openrct2-build:13-focal32
|
||||
build_flags: -DFORCE32=ON -DENABLE_SCRIPTING=OFF -DCMAKE_CXX_FLAGS="-m32 -g1 -gz" -DWITH_TESTS=off
|
||||
distro: Ubuntu
|
||||
release: jammy
|
||||
image: openrct2/openrct2-build:14-jammy32
|
||||
build_flags: -DFORCE32=ON -DENABLE_SCRIPTING=OFF -DCMAKE_CXX_FLAGS="-m32 -g -gz" -DWITH_TESTS=off
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
@ -353,10 +351,10 @@ jobs:
|
|||
echo 'Not going to push build'
|
||||
fi
|
||||
linux-appimage:
|
||||
name: Linux (x86_64, AppImage)
|
||||
name: Ubuntu Linux (AppImage, x86_64)
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-code-formatting
|
||||
container: openrct2/openrct2-build:13-focal
|
||||
container: openrct2/openrct2-build:14-jammy
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
@ -386,7 +384,7 @@ jobs:
|
|||
echo 'Not going to push build'
|
||||
fi
|
||||
linux-docker:
|
||||
name: Linux (docker)
|
||||
name: Ubuntu Linux (Docker)
|
||||
needs: check-code-formatting
|
||||
if: github.repository == 'OpenRCT2/OpenRCT2'
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -411,10 +409,10 @@ jobs:
|
|||
echo 'Image not pushed'
|
||||
fi
|
||||
linux-clang:
|
||||
name: Linux (Debug, [http, network, flac, vorbis OpenGL] disabled) using clang
|
||||
name: Ubuntu Linux (jammy, debug, [http, network, flac, vorbis OpenGL] disabled) using clang
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-code-formatting
|
||||
container: openrct2/openrct2-build:13-jammy
|
||||
container: openrct2/openrct2-build:14-jammy
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
@ -427,10 +425,10 @@ jobs:
|
|||
- name: Build OpenRCT2
|
||||
run: . scripts/setenv && build -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Debug -DDISABLE_NETWORK=ON -DDISABLE_HTTP=ON -DDISABLE_FLAC=ON -DDISABLE_VORBIS=ON -DDISABLE_OPENGL=ON
|
||||
linux-clang-tests:
|
||||
name: Linux (Debug) using clang, coverage enabled
|
||||
name: Ubuntu Linux (debug) using clang, coverage enabled
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-code-formatting
|
||||
container: openrct2/openrct2-build:13-jammy
|
||||
container: openrct2/openrct2-build:14-jammy
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
@ -471,7 +469,7 @@ jobs:
|
|||
name: Android
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-code-formatting
|
||||
container: openrct2/openrct2-build:13-android
|
||||
container: openrct2/openrct2-build:14-android
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
|
|
@ -64,9 +64,9 @@ set(TITLE_SEQUENCE_VERSION "0.4.6")
|
|||
set(TITLE_SEQUENCE_URL "https://github.com/OpenRCT2/title-sequences/releases/download/v${TITLE_SEQUENCE_VERSION}/title-sequences.zip")
|
||||
set(TITLE_SEQUENCE_SHA1 "80fefc6ebbabc42a6f4703412daa5c62f661420d")
|
||||
|
||||
set(OBJECTS_VERSION "1.4.0")
|
||||
set(OBJECTS_VERSION "1.4.3")
|
||||
set(OBJECTS_URL "https://github.com/OpenRCT2/objects/releases/download/v${OBJECTS_VERSION}/objects.zip")
|
||||
set(OBJECTS_SHA1 "f512e230ffa2f16109209e4f30e7d7bf4fe21fc0")
|
||||
set(OBJECTS_SHA1 "ac78210ef46465c0f51bbffd6fe21845092af48e")
|
||||
|
||||
set(OPENSFX_VERSION "1.0.5")
|
||||
set(OPENSFX_URL "https://github.com/OpenRCT2/OpenSoundEffects/releases/download/v${OPENSFX_VERSION}/opensound.zip")
|
||||
|
@ -277,7 +277,7 @@ if (MSVC)
|
|||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4244") # C4244: 'conversion_type': conversion from 'type1' to 'type2', possible loss of data
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4068") # C4068: unknown pragma
|
||||
|
||||
# Enable char8_t<->char conversion :(
|
||||
# Enable char8_t<->char conversion
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:char8_t-")
|
||||
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
|
@ -334,7 +334,7 @@ else ()
|
|||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-pragmas -Wno-missing-braces -Wno-comment -Wnonnull -Wno-unused-parameter -Wno-attributes")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDEBUG=${DEBUG_LEVEL}")
|
||||
|
||||
# Enable char8_t<->char conversion :(
|
||||
# Enable char8_t<->char conversion
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-char8_t -Wno-deprecated-declarations")
|
||||
|
||||
if(APPLE)
|
||||
|
|
|
@ -121,6 +121,7 @@ Appreciation for contributors who have provided substantial work, but are no lon
|
|||
* Alex Parisi (alex-parisi) - Added API for returning metadata from all registered plugins.
|
||||
|
||||
## Bug fixes & Refactors
|
||||
* Claudio Tiecher (janclod)
|
||||
* (KirilAngelov)
|
||||
* (halfbro)
|
||||
* (Myrtle)
|
||||
|
@ -234,6 +235,8 @@ Appreciation for contributors who have provided substantial work, but are no lon
|
|||
* Michael Bernardi (mrmbernardi)
|
||||
* Aram Kazorian (aramk-hub)
|
||||
* Harry Hopkinson (Harry-Hopkinson)
|
||||
* Jan Kelemen (jan-kelemen)
|
||||
* Cory Ye (CoryfY)
|
||||
|
||||
## Toolchain
|
||||
* (Balletie) - macOS
|
||||
|
|
|
@ -1740,12 +1740,12 @@ STR_2382 :Land
|
|||
STR_2383 :Water
|
||||
STR_2384 :{WINDOW_COLOUR_2}Your objective:
|
||||
STR_2385 :{BLACK}None
|
||||
STR_2386 :{BLACK}To have at least {COMMA16} guests in your park at the end of {MONTHYEAR}, with a park rating of at least 600
|
||||
STR_2386 :{BLACK}To have at least {COMMA32} guests in your park at the end of {MONTHYEAR}, with a park rating of at least 600
|
||||
STR_2387 :{BLACK}To achieve a park value of at least {POP16}{POP16}{CURRENCY} at the end of {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}
|
||||
STR_2388 :{BLACK}Have Fun!
|
||||
STR_2389 :{BLACK}Build the best {STRINGID} you can!
|
||||
STR_2390 :{BLACK}To have 10 different types of roller coasters operating in your park, each with an excitement value of at least 6.00
|
||||
STR_2391 :{BLACK}To have at least {COMMA16} guests in your park. You must not let the park rating drop below 700 at any time!
|
||||
STR_2391 :{BLACK}To have at least {COMMA32} guests in your park. You must not let the park rating drop below 700 at any time!
|
||||
STR_2392 :{BLACK}To achieve a monthly income from ride tickets of at least {POP16}{POP16}{CURRENCY}
|
||||
STR_2393 :{BLACK}To have 10 different types of roller coasters operating in your park, each with a minimum length of {LENGTH}, and an excitement rating of at least 7.00
|
||||
STR_2394 :{BLACK}To finish building all 5 of the partially built roller coasters in this park, designing them to achieve excitement ratings of at least {POP16}{POP16}{COMMA2DP32} each
|
||||
|
@ -2326,7 +2326,7 @@ STR_3305 :{WINDOW_COLOUR_2}Monthly income:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Monthly profit:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Minimum length:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Excitement rating:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Rides/attractions under a preservation order:
|
||||
|
|
|
@ -1740,12 +1740,12 @@ STR_2382 :Terra
|
|||
STR_2383 :Aigua
|
||||
STR_2384 :{WINDOW_COLOUR_2}El vostre objectiu:
|
||||
STR_2385 :{BLACK}Cap
|
||||
STR_2386 :{BLACK}Tenir almenys {COMMA16} visitants al parc al final de {MONTHYEAR}, amb una valoració del parc d’almenys 600.
|
||||
STR_2386 :{BLACK}Tenir almenys {COMMA32} visitants al parc al final de {MONTHYEAR}, amb una valoració del parc d’almenys 600.
|
||||
STR_2387 :{BLACK}Aconseguir una valoració del parc d’almenys {POP16}{POP16}{CURRENCY} al final de {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}.
|
||||
STR_2388 :{BLACK}Divertiu-vos!
|
||||
STR_2389 :{BLACK}Construïu el millor {STRINGID} que pugueu!
|
||||
STR_2390 :{BLACK}Tenir 10 tipus diferents de muntanyes russes operant al parc, cadascuna d’elles amb un valor d’emoció d’almenys 6,00.
|
||||
STR_2391 :{BLACK}Tenir almenys {COMMA16} visitants al parc. No podeu deixar que la valoració del parc caigui per sota de 700 en cap moment!
|
||||
STR_2391 :{BLACK}Tenir almenys {COMMA32} visitants al parc. No podeu deixar que la valoració del parc caigui per sota de 700 en cap moment!
|
||||
STR_2392 :{BLACK}Aconseguir uns ingressos mensuals amb les entrades d’atraccions d’almenys {POP16}{POP16}{CURRENCY}.
|
||||
STR_2393 :{BLACK}Tenir 10 tipus diferents de muntanyes russes operant al parc, cadascuna amb una longitud mínima de {LENGTH} i un valor d’emoció d’almenys 7,00.
|
||||
STR_2394 :{BLACK}Acabar de construir les cinc muntanyes russes que s’havien començat a construir al parc, dissenyant-les per a aconseguir un nivell d’emoció d’almenys {POP16}{POP16}{COMMA2DP32} per cada una d’elles.
|
||||
|
@ -2326,7 +2326,7 @@ STR_3305 :{WINDOW_COLOUR_2}Ingressos mensuals:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Beneficis mensuals:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Longitud mínima:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Nivell d’emoció:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Atraccions protegides:
|
||||
|
@ -3687,6 +3687,10 @@ STR_6610 :L’element de camí no s’ha trobat
|
|||
STR_6611 :L’element de mur no s’ha trobat
|
||||
STR_6612 :L’element de text de cartell no s’ha trobat
|
||||
STR_6613 :Recarrega l’objecte
|
||||
STR_6614 :El preu de l’entrada al parc no es pot canviar
|
||||
STR_6615 :La via d'aquesta casella necessita aigua
|
||||
STR_6616 :L’acció no és vàlida per a aquest tipus d’empleat
|
||||
STR_6617 :No es pot intercanviar l’element de la casella amb si mateix
|
||||
|
||||
#############
|
||||
# Scenarios #
|
||||
|
|
|
@ -1742,12 +1742,12 @@ STR_2382 :Země
|
|||
STR_2383 :Vodní plochy
|
||||
STR_2384 :{WINDOW_COLOUR_2}Cíl scénáře:
|
||||
STR_2385 :{BLACK}žádný
|
||||
STR_2386 :{BLACK}Mít v parku alespoň {COMMA16} návštěvníků na konci {MONTHYEAR}, s hodnocením parku alespoň 600
|
||||
STR_2386 :{BLACK}Mít v parku alespoň {COMMA32} návštěvníků na konci {MONTHYEAR}, s hodnocením parku alespoň 600
|
||||
STR_2387 :{BLACK}Dosáhnout hodnoty parku alespoň {POP16}{POP16}{CURRENCY} na konci {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}
|
||||
STR_2388 :{BLACK}Příjemnou zábavu!
|
||||
STR_2389 :{BLACK}Postav ten nejlepší {STRINGID}!
|
||||
STR_2390 :{BLACK}Mít v parku 10 druhů kolotočů v provozu, každý s hodnotou zábavnosti alespoň 6.00
|
||||
STR_2391 :{BLACK}Mít v parku alespoň {COMMA16} návštěvníků a zároveň nikdy nenechat hodnocení parku spadnout pod 700!
|
||||
STR_2391 :{BLACK}Mít v parku alespoň {COMMA32} návštěvníků a zároveň nikdy nenechat hodnocení parku spadnout pod 700!
|
||||
STR_2392 :{BLACK}Dosáhnout měsíčního příjmu z lístků na atrakce alespoň {POP16}{POP16}{CURRENCY}
|
||||
STR_2393 :{BLACK}{BLACK}Mít v parku 10 druhů kolotočů v provozu, každý s hodnotou zábavnosti alespoň 7.00 a délkou alespoň {LENGHT}
|
||||
STR_2394 :{BLACK}Dokončit všech 5 rozestavěných horských drah tak, aby každá dosahovala hodnocení vzrušení alespoň {POP16}{POP16}{COMMA2DP32}
|
||||
|
@ -2328,7 +2328,7 @@ STR_3305 :{WINDOW_COLOUR_2}Měsíční příjem:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Měsíční zisk:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Minimální délka:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Nadšení:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Atrakce, které je nutno zachovat:
|
||||
|
|
|
@ -1737,12 +1737,12 @@ STR_2382 :Land
|
|||
STR_2383 :Vand
|
||||
STR_2384 :{WINDOW_COLOUR_2}Dit mål:
|
||||
STR_2385 :{BLACK}Ingen
|
||||
STR_2386 :{BLACK}At have mindst {COMMA16} gæster i parken, ved udgangen af {MONTHYEAR}, og med en park vurdering på mindst 600
|
||||
STR_2386 :{BLACK}At have mindst {COMMA32} gæster i parken, ved udgangen af {MONTHYEAR}, og med en park vurdering på mindst 600
|
||||
STR_2387 :{BLACK}At opnå en Parkværdi på mindst {POP16}{POP16}{CURRENCY} ved udgangen af {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}
|
||||
STR_2388 :{BLACK}Hav det sjovt!
|
||||
STR_2389 :{BLACK}Byg den bedste {STRINGID} du kan!
|
||||
STR_2390 :{BLACK}At have 10 forskellige typer rutschebaner i gang i parken på samme tid, Hver med en spændings vurdering over 6.00
|
||||
STR_2391 :{BLACK}At have mindst {COMMA16} gæster i parken. Du må ikke lade park vurderingen flade under 700 på noget tidspunkt!
|
||||
STR_2391 :{BLACK}At have mindst {COMMA32} gæster i parken. Du må ikke lade park vurderingen flade under 700 på noget tidspunkt!
|
||||
STR_2392 :{BLACK}At opnå en månedlig indkomst fra forlystelser på mindst {POP16}{POP16}{CURRENCY}
|
||||
STR_2393 :{BLACK}At have 10 forskellige typer rutschebaner i gang i parken på samme tid, hver med en minimums længde på {LENGTH}, og en spændings vurdering på mindst 7.00
|
||||
STR_2394 :{BLACK}At færdiggøre alle 5 af de delvist færdige byggede rutschebaner i denne park, design dem til at have at opnå en spændings vurdering på mindst {POP16}{POP16}{COMMA2DP32} hver
|
||||
|
@ -2323,7 +2323,7 @@ STR_3305 :{WINDOW_COLOUR_2}Månedsinkomst:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Månedsprofit:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Minimum længde:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Spændings vurdering:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}forlystelser/attraktioner under bevaringsordre:
|
||||
|
|
|
@ -1738,12 +1738,12 @@ STR_2382 :Land
|
|||
STR_2383 :Wasser
|
||||
STR_2384 :{WINDOW_COLOUR_2}Ihr Ziel:
|
||||
STR_2385 :{BLACK}Keins
|
||||
STR_2386 :{BLACK}Mindestens {COMMA16} Parkbesucher bis {MONTHYEAR} und eine Parkbewertung von mindestens 600 zu haben
|
||||
STR_2386 :{BLACK}Mindestens {COMMA32} Parkbesucher bis {MONTHYEAR} und eine Parkbewertung von mindestens 600 zu haben
|
||||
STR_2387 :{BLACK}Einen Verkehrswert von mindestens {POP16}{POP16}{CURRENCY} zu erreichen bis {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}
|
||||
STR_2388 :{BLACK}Viel Spaß!
|
||||
STR_2389 :{BLACK}Bauen Sie Ihre(n) beste(n) {STRINGID}!
|
||||
STR_2390 :{BLACK}Mindestens 10 verschiedene Achterbahntypen in Ihrem Park zu betreiben, wobei jeder einen Nervenkitzelwert von mindestens 6,00 aufweisen muss
|
||||
STR_2391 :{BLACK}Mindestens {COMMA16} Parkbesucher zu erreichen. Die Parkbewertung darf zu keinem Zeitpunkt unter 700 fallen!
|
||||
STR_2391 :{BLACK}Mindestens {COMMA32} Parkbesucher zu erreichen. Die Parkbewertung darf zu keinem Zeitpunkt unter 700 fallen!
|
||||
STR_2392 :{BLACK}Monatliche Einkünfte durch Fahrttickets von mindestens {POP16}{POP16}{CURRENCY} zu erreichen
|
||||
STR_2393 :{BLACK}Mindestens 10 verschiedene Achterbahntypen in Ihrem Park zu betreiben, wobei jeder eine Mindestlänge von {LENGTH} und einen Nervenkitzelwert von mindestens 7,00 aufweisen muss
|
||||
STR_2394 :{BLACK}Alle 5 bereits teilweise gebauten Achterbahnen fertigzustellen, damit sie jeweils einen Nervenkitzelwert von {POP16}{POP16}{COMMA2DP32} erzielen
|
||||
|
@ -2325,7 +2325,7 @@ STR_3305 :{WINDOW_COLOUR_2}Monatliche Einkünfte:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Monatlicher Gewinn:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Mindestlänge:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Nervenkitzelwert:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Attraktionen unter Beibehaltungsauftrag:
|
||||
|
@ -3678,6 +3678,16 @@ STR_6609 :Streckenblock konnte nicht gefunden werden
|
|||
STR_6610 :Wegelement konnte nicht gefunden werden
|
||||
STR_6611 :Wandelement konnte nicht gefunden werden
|
||||
STR_6612 :Bannerelement konnte nicht gefunden werden
|
||||
STR_6613 :Objekt neu laden
|
||||
STR_6614 :Parkeintrittsgeld kann nicht geändert werden
|
||||
STR_6615 :Streckenabschnitt auf dieser Kachel benötigt Wasser
|
||||
STR_6616 :Aktion ungültig für diesen Personaltyp
|
||||
STR_6617 :Kachelelement kann nicht mit sich selbst vertauscht werden
|
||||
STR_6618 :Objekt kann weder eingeschränkt werden noch kann die Einschränkung zurückgenommen werden …
|
||||
STR_6619 :Objekttyp kann nicht eingeschränkt werden!
|
||||
STR_6620 :Objekt nicht gefunden!
|
||||
STR_6621 :Einschränken
|
||||
STR_6622 :Objekt auf den Szenario-Editor und dem Sandkastenmodus einschränken
|
||||
|
||||
#############
|
||||
# Scenarios #
|
||||
|
|
|
@ -98,6 +98,7 @@ STR_0093 :Hybrid Coaster
|
|||
STR_0094 :Single Rail Roller Coaster
|
||||
STR_0095 :Alpine Coaster
|
||||
STR_0096 :Classic Wooden Roller Coaster
|
||||
STR_0097 :Classic Stand-up Roller Coaster
|
||||
STR_0512 :A compact roller coaster with a spiral lift hill and smooth, twisting drops.
|
||||
STR_0513 :A looping roller coaster where the riders ride in a standing position
|
||||
STR_0514 :Trains suspended beneath the roller coaster track swing out to the side around corners
|
||||
|
@ -181,6 +182,7 @@ STR_0603 :A wooden style roller coaster with a steel track, allowing for stee
|
|||
STR_0604 :Riders ride single file on a narrow monorail track, as they race through tight inversions and direction changes
|
||||
STR_0605 :Riders toboggan down a meandering steel track, braking to control their speed
|
||||
STR_0606 :An older-style wooden roller coaster with a fast and rough ride, with plenty of air-time, some lateral G’s, and designed to feel ‘out-of-control’
|
||||
STR_0607 :An intense, older-style steel looping roller coaster where the riders ride in a standing position
|
||||
STR_0767 :Guest {INT32}
|
||||
STR_0768 :Handyman {INT32}
|
||||
STR_0769 :Mechanic {INT32}
|
||||
|
@ -1737,12 +1739,12 @@ STR_2382 :Land
|
|||
STR_2383 :Water
|
||||
STR_2384 :{WINDOW_COLOUR_2}Your objective:
|
||||
STR_2385 :{BLACK}None
|
||||
STR_2386 :{BLACK}To have at least {COMMA16} guests in your park at the end of {MONTHYEAR}, with a park rating of at least 600
|
||||
STR_2386 :{BLACK}To have at least {COMMA32} guests in your park at the end of {MONTHYEAR}, with a park rating of at least 600
|
||||
STR_2387 :{BLACK}To achieve a park value of at least {POP16}{POP16}{CURRENCY} at the end of {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}
|
||||
STR_2388 :{BLACK}Have Fun!
|
||||
STR_2389 :{BLACK}Build the best {STRINGID} you can!
|
||||
STR_2390 :{BLACK}To have 10 different types of roller coasters operating in your park, each with an excitement value of at least 6.00
|
||||
STR_2391 :{BLACK}To have at least {COMMA16} guests in your park. You must not let the park rating drop below 700 at any time!
|
||||
STR_2391 :{BLACK}To have at least {COMMA32} guests in your park. You must not let the park rating drop below 700 at any time!
|
||||
STR_2392 :{BLACK}To achieve a monthly income from ride tickets of at least {POP16}{POP16}{CURRENCY}
|
||||
STR_2393 :{BLACK}To have 10 different types of roller coasters operating in your park, each with a minimum length of {LENGTH}, and an excitement rating of at least 7.00
|
||||
STR_2394 :{BLACK}To finish building all 5 of the partially built roller coasters in this park, designing them to achieve excitement ratings of at least {POP16}{POP16}{COMMA2DP32} each
|
||||
|
@ -2323,7 +2325,7 @@ STR_3305 :{WINDOW_COLOUR_2}Monthly income:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Monthly profit:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Minimum length:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Excitement rating:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Rides/attractions under a preservation order:
|
||||
|
@ -3692,6 +3694,9 @@ STR_6619 :Object type cannot be restricted!
|
|||
STR_6620 :Object not found!
|
||||
STR_6621 :Restrict
|
||||
STR_6622 :Restrict object to the Scenario Editor and Sandbox mode.
|
||||
STR_6623 :Type ‘help’ for a list of available commands. Type ‘hide’ to hide the console.
|
||||
STR_6624 :Tile Inspector: Sort elements
|
||||
STR_6625 :Invalid colour
|
||||
|
||||
#############
|
||||
# Scenarios #
|
||||
|
|
|
@ -7,12 +7,11 @@
|
|||
# Vanilla strings. Only change strings that differ from UK English!
|
||||
STR_0007 :Miniature Railroad
|
||||
STR_0015 :Bobsled Coaster
|
||||
STR_0027 :Bumper Cars
|
||||
STR_0035 :Carousel
|
||||
STR_0038 :Restroom
|
||||
STR_0041 :3D Theater
|
||||
STR_0045 :Elevator
|
||||
STR_0047 :ATM
|
||||
STR_0052 :Tunnel Of Horror
|
||||
STR_0517 :Passengers ride in miniature trains along a narrow-gauge railroad track
|
||||
STR_0529 :Mine train themed roller coaster trains career along steel roller coaster track made to look like old railroad track
|
||||
STR_0537 :Guests bump into each other in self-driven electric bumper cars
|
||||
|
@ -203,7 +202,7 @@ STR_3102 :Re-paint colored scenery on landscape
|
|||
STR_3122 :{WINDOW_COLOUR_2}Favorite ride of: {BLACK}{COMMA32} guest
|
||||
STR_3123 :{WINDOW_COLOUR_2}Favorite ride of: {BLACK}{COMMA32} guests
|
||||
|
||||
STR_3372 :{BLACK}= A.T.M.
|
||||
STR_3372 :{BLACK}= ATM
|
||||
STR_3373 :{BLACK}= Restroom
|
||||
|
||||
# Strings added by OpenRCT2. Only add strings that differ from UK English!
|
||||
|
|
|
@ -1737,12 +1737,12 @@ STR_2382 :Tero
|
|||
STR_2383 :Akvo
|
||||
STR_2384 :{WINDOW_COLOUR_2}Via celo:
|
||||
STR_2385 :{BLACK}Neniu
|
||||
STR_2386 :{BLACK}Havi almenaŭ {COMMA16} gastojn en via parko al la fino de {MONTHYEAR}, kun pritakso de parko ne malpli ol 600
|
||||
STR_2386 :{BLACK}Havi almenaŭ {COMMA32} gastojn en via parko al la fino de {MONTHYEAR}, kun pritakso de parko ne malpli ol 600
|
||||
STR_2387 :{BLACK}Atingi parkvaloron ne malpli ol {POP16}{POP16}{CURRENCY} al la fino de {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}
|
||||
STR_2388 :{BLACK}Amuzi Vin!
|
||||
STR_2389 :{BLACK}Konstrui je la plej bona {STRINGID}, kiun vi povas konstrui!
|
||||
STR_2390 :{BLACK}Havi 10 malsamajn tipojn de ondaj fervojoj funkciantajn en via parko, kiuj havas po pritakson de eksciteco ne malpli ol 6.00
|
||||
STR_2391 :{BLACK}Havi almenaŭ {COMMA16} gastojn en via parko. Ankaŭ, necesas ke la pritakso de la parko neniam falu sub 700!
|
||||
STR_2391 :{BLACK}Havi almenaŭ {COMMA32} gastojn en via parko. Ankaŭ, necesas ke la pritakso de la parko neniam falu sub 700!
|
||||
STR_2392 :{BLACK}Atingi enspezon monatan ne malpli ol {POP16}{POP16}{CURRENCY}, pro la vendado de biletoj de atrakcioj
|
||||
STR_2393 :{BLACK}Havi 10 malsamajn tipojn de ondaj fervojoj funkciantajn en via parko, kiuj havas po mimimuman longecon de {LENGTH} kaj pritakson de eksciteco ne malpli ol 7.00
|
||||
STR_2394 :{BLACK}Fini la konstruadon de la tutaj 5 ondaj fervojoj konstruitaj parte. Desegni ilin, por atingi po pritakson de eksciteco ne malpli ol {POP16}{POP16}{COMMA2DP32}
|
||||
|
@ -2323,7 +2323,7 @@ STR_3305 :{WINDOW_COLOUR_2}Enspezo monata:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Profito monata:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Minimuma longeco:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Pritakso de eksciteco:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Atrakcioj, kiujn oni ne eblas ŝanĝi dum la scenaro:
|
||||
|
@ -3686,6 +3686,11 @@ STR_6614 :Ne povas ŝanĝi enirpagon de parko
|
|||
STR_6615 :Trako sur ĉi tiu bloko bezonas akvon
|
||||
STR_6616 :Ago estas nevalida por tiu dungitotipo
|
||||
STR_6617 :Ne povas permuti blokelementon kun si mem
|
||||
STR_6618 :Ne povas limigi aŭ mallimigi objekton…
|
||||
STR_6619 :Objekotipo ne povas esti limigita!
|
||||
STR_6620 :Objekto ne troviĝis!
|
||||
STR_6621 :Limigi
|
||||
STR_6622 :Limigi objekton al la Scenaroredaktilo kaj Provejo-reĝimo.
|
||||
|
||||
#############
|
||||
# Scenarios #
|
||||
|
|
|
@ -1738,12 +1738,12 @@ STR_2382 :Tierra
|
|||
STR_2383 :Agua
|
||||
STR_2384 :{WINDOW_COLOUR_2}Tu objetivo:
|
||||
STR_2385 :{BLACK}Ninguno
|
||||
STR_2386 :{BLACK}Tener al menos {COMMA16} visitantes en tu parque a finales de {MONTHYEAR}, con una valoración del parque de al menos 600
|
||||
STR_2386 :{BLACK}Tener al menos {COMMA32} visitantes en tu parque a finales de {MONTHYEAR}, con una valoración del parque de al menos 600
|
||||
STR_2387 :{BLACK}Conseguir un valor del parque {POP16}{POP16}{CURRENCY} a final de {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}
|
||||
STR_2388 :{BLACK}¡Diviértete!
|
||||
STR_2389 :{BLACK}¡Construye el mejor {STRINGID} que puedas!
|
||||
STR_2390 :{BLACK}Tener al menos 10 tipos de montañas rusas distintas operando en el parque, cada una con un valor de emoción de al menos 6.00
|
||||
STR_2391 :{BLACK}Tener al menos {COMMA16} visitantes en tu parque. ¡No dejes que la valoración baje de 700 en ningún momento!
|
||||
STR_2391 :{BLACK}Tener al menos {COMMA32} visitantes en tu parque. ¡No dejes que la valoración baje de 700 en ningún momento!
|
||||
STR_2392 :{BLACK}Conseguir unos ingresos mensuales por venta de entradas a atracciones de al menos {POP16}{POP16}{CURRENCY}
|
||||
STR_2393 :{BLACK}Tener al menos 10 tipos de montañas rusas distintas operando en el parque, cada una con una longitud mínima de {LENGTH}, y un valor de emoción de al menos 7.00
|
||||
STR_2394 :{BLACK}Terminar de construir 5 de las montañas rusas no terminadas en este parque, diseñándolas de modo que tengan un valor de emoción de al menos {POP16}{POP16}{COMMA2DP32} cada una
|
||||
|
@ -2324,7 +2324,7 @@ STR_3305 :{WINDOW_COLOUR_2}Ingreso mensual:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Ganancia mensual:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Longitud mínima:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Rango Satisfacción:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Atracciones/Juegos con orden de preservación:
|
||||
|
@ -3686,6 +3686,15 @@ STR_6610 :Elemento de camino no encontrado
|
|||
STR_6611 :Elemento de muro no encontrado
|
||||
STR_6612 :Elemento de banner no encontrado
|
||||
STR_6613 :Recargar objeto
|
||||
STR_6614 :No se puede cambiar el precio de la entrada al parque
|
||||
STR_6615 :La vía en esta casilla necesita agua
|
||||
STR_6616 :Acción no válida para ese tipo de trabajador
|
||||
STR_6617 :No se puede intercambiar el elemento de la casilla consigo mismo
|
||||
STR_6618 :No se puede poner ni quitar restricción al objeto…
|
||||
STR_6619 :¡Este tipo de objeto no puede ser restringido!
|
||||
STR_6620 :¡Objeto no encontrado!
|
||||
STR_6621 :Restringir
|
||||
STR_6622 :Restringir objeto al Editor de Escenarios y el modo Sandbox.
|
||||
|
||||
##############
|
||||
# Escenarios #
|
||||
|
|
|
@ -1743,12 +1743,12 @@ STR_2382 :Maa
|
|||
STR_2383 :Vesi
|
||||
STR_2384 :{WINDOW_COLOUR_2}Tavoitteesi:
|
||||
STR_2385 :{BLACK}Ei mitään
|
||||
STR_2386 :{BLACK}Puistossasi tulee olla vähintään {COMMA16} kävijää {MONTHYEAR} loppuun mennessä, ja puiston tason tulee olla vähintään 600
|
||||
STR_2386 :{BLACK}Puistossasi tulee olla vähintään {COMMA32} kävijää {MONTHYEAR} loppuun mennessä, ja puiston tason tulee olla vähintään 600
|
||||
STR_2387 :{BLACK}Saavuta puiston arvoksi vähintään {POP16}{POP16}{CURRENCY} {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR} loppuun mennessä
|
||||
STR_2388 :{BLACK}Pidä hauskaa!
|
||||
STR_2389 :{BLACK}Rakenna niin hyvä {STRINGID} kuin voit!
|
||||
STR_2390 :{BLACK}Puistossasi tulee olla 10 eri tyyppistä toiminnassa olevaa vuoristorataa joiden huvitustaso on vähintään 6.00
|
||||
STR_2391 :{BLACK}Puistossasi tulee olla vähintään {COMMA16} kävijää, eikä puiston taso saa pudota hetkeksikään alle 700:n!
|
||||
STR_2391 :{BLACK}Puistossasi tulee olla vähintään {COMMA32} kävijää, eikä puiston taso saa pudota hetkeksikään alle 700:n!
|
||||
STR_2392 :{BLACK}Sinun tulee ansaita {POP16}{POP16}{CURRENCY} kuukausituloja laitteiden lipputuloista
|
||||
STR_2393 :{BLACK}Puistossasi tulee olla 10 eri tyyppistä toiminnassa olevaa vuoristorataa, joiden pitää olla vähintään {LENGTH} pitkiä ja joiden huvitustaso on vähintään 7.00
|
||||
STR_2394 :{BLACK}Viimeistele puiston 5 osittain rakennettua vuoristorataa niin, että jokaisen huvitustaso on vähintään {POP16}{POP16}{COMMA2DP32}
|
||||
|
@ -2329,7 +2329,7 @@ STR_3305 :{WINDOW_COLOUR_2}Kuukausittainen tulo:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Kuukausittainen tuotto:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Vähimmäispituus:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Huvitustaso:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Laitteet suojelumääräyksen alla:
|
||||
|
|
|
@ -1744,12 +1744,12 @@ STR_2382 :Terrain
|
|||
STR_2383 :Eau
|
||||
STR_2384 :{WINDOW_COLOUR_2}Votre objectif :
|
||||
STR_2385 :{BLACK}Aucun
|
||||
STR_2386 :{BLACK}Avoir au moins {COMMA16} visiteurs dans votre parc à la fin {MONTHYEAR}, avec une évaluation de parc d’au moins 600
|
||||
STR_2386 :{BLACK}Avoir au moins {COMMA32} visiteurs dans votre parc à la fin {MONTHYEAR}, avec une évaluation de parc d’au moins 600
|
||||
STR_2387 :{BLACK}Atteindre une valeur de parc d’au moins {POP16}{POP16}{CURRENCY} à la fin {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}
|
||||
STR_2388 :{BLACK}Amusez-vous !
|
||||
STR_2389 :{BLACK}Construisez la meilleure attraction {STRINGID} possible !
|
||||
STR_2390 :{BLACK}Avoir construit au moins 10 montagnes russes différentes dans votre parc, chacune d’entre elles atteignant un indice d’engouement d’au moins 6.00
|
||||
STR_2391 :{BLACK}Avoir reçu au moins {COMMA16} visiteurs dans votre parc. L’évaluation du parc ne doit jamais descendre en dessous des 700 !
|
||||
STR_2391 :{BLACK}Avoir reçu au moins {COMMA32} visiteurs dans votre parc. L’évaluation du parc ne doit jamais descendre en dessous des 700 !
|
||||
STR_2392 :{BLACK}Obtenir un revenu mensuel avec les tickets d’attraction d’au moins {POP16}{POP16}{CURRENCY}
|
||||
STR_2393 :{BLACK}Avoir construit au moins 10 montagnes russes différentes dans votre parc, chacune d’entre elles atteignant une longueur d’au moins {LENGTH}, avec un indice d’engouement d’au moins 7.00
|
||||
STR_2394 :{BLACK}Terminer la construction des 5 montagnes russes entamées dans ce parc, avec une conception leur permettant d’atteindre un indice d’engouement d’au moins {POP16}{POP16}{COMMA2DP32} chacune
|
||||
|
@ -2330,7 +2330,7 @@ STR_3305 :{WINDOW_COLOUR_2}Revenu mensuel :
|
|||
STR_3306 :{WINDOW_COLOUR_2}Bénéfice mensuel :
|
||||
STR_3307 :{WINDOW_COLOUR_2}Longueur minimum :
|
||||
STR_3308 :{WINDOW_COLOUR_2}Indice d’engouement :
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Attractions protégées :
|
||||
|
@ -3691,6 +3691,15 @@ STR_6610 :Élément de chemin introuvable
|
|||
STR_6611 :Élément de mur introuvable
|
||||
STR_6612 :Élément de bannière introuvable
|
||||
STR_6613 :Recharger l’objet
|
||||
STR_6614 :Impossible de changer le prix d’entrée du parc
|
||||
STR_6615 :La voie a besoin d’eau sur cette case
|
||||
STR_6616 :Action invalide pour ce type de personnel
|
||||
STR_6617 :Impossible de permuter un élément de case avec lui-même
|
||||
STR_6618 :Impossible de restreindre ou dé-restreindre l’objet…
|
||||
STR_6619 :Ce type d’objet ne peut pas être restreint !
|
||||
STR_6620 :Objet introuvable !
|
||||
STR_6621 :Restreindre
|
||||
STR_6622 :Restreint l’objet à l’éditeur de scénario et au mode bac à sable
|
||||
|
||||
#############
|
||||
# Scenarios #
|
||||
|
|
|
@ -14,7 +14,7 @@ STR_0009 :Mini felfüggesztett hullámvasút
|
|||
STR_0010 :Csónakkölcsönző
|
||||
STR_0011 :Fa vadegér
|
||||
STR_0012 :Akadályverseny
|
||||
STR_0013 :Autós utazás
|
||||
STR_0013 :Autós játék
|
||||
STR_0014 :Kilövéses szabadesés
|
||||
STR_0015 :Bobpálya hullámvasút
|
||||
STR_0016 :Megfigyelőtorony
|
||||
|
@ -27,7 +27,7 @@ STR_0022 :Útvesztő
|
|||
STR_0023 :Spirál csúszda
|
||||
STR_0024 :Gokart
|
||||
STR_0025 :Faúsztató
|
||||
STR_0026 :Vadvízi utazás
|
||||
STR_0026 :Vadvízi játék
|
||||
STR_0027 :Dodzsem
|
||||
STR_0028 :Hajóhinta
|
||||
STR_0029 :Fejreálló hajóhinta
|
||||
|
@ -81,7 +81,7 @@ STR_0076 :Vízi hullámvasút
|
|||
STR_0077 :Léghajtású függőleges hullámvasút
|
||||
STR_0078 :Fordított hajtű hullámvasút
|
||||
STR_0079 :Varázsszőnyeg
|
||||
STR_0080 :Tengeralattjáró-utazás
|
||||
STR_0080 :Tengeralattjáró-játék
|
||||
STR_0081 :Folyami tutajok
|
||||
STR_0082 :Ismeretlen játék (50)
|
||||
STR_0083 :Enterprise
|
||||
|
@ -326,7 +326,7 @@ STR_0934 :A játék bejárata útban van
|
|||
STR_0935 :A játék kijárata útban van
|
||||
STR_0936 :A park bejárata útban van
|
||||
STR_0937 :Nézet beállítások
|
||||
STR_0938 :A föld magasságának és lejtésének módosítása
|
||||
STR_0938 :A talaj magasságának és lejtésének módosítása
|
||||
STR_0939 :Földalatti/belső nézet
|
||||
STR_0940 :Alaptalaj elrejtése
|
||||
STR_0941 :Függőleges felületek elrejtése
|
||||
|
@ -543,7 +543,7 @@ STR_1150 :Háromnegyed terhelés
|
|||
STR_1151 :Teljes terhelés
|
||||
STR_1152 :Bármekkora terhelés
|
||||
STR_1153 :Magasságjelzők a játékpályákon
|
||||
STR_1154 :Magasságjelzők a terepen
|
||||
STR_1154 :Magasságjelzők a talajon
|
||||
STR_1155 :Magasságjelzők az utakon
|
||||
STR_1156 :{MOVE_X}{10}{STRINGID}
|
||||
STR_1157 :✓{MOVE_X}{10}{STRINGID}
|
||||
|
@ -1256,12 +1256,12 @@ STR_1875 :{BLACK} {SPRITE}{BLACK} {STRINGID}
|
|||
STR_1876 :{WINDOW_COLOUR_2}{INLINE_SPRITE}{251}{19}{00}{00}Játékok ellenőrzése
|
||||
STR_1877 :{WINDOW_COLOUR_2}{INLINE_SPRITE}{252}{19}{00}{00}Játékok javítása
|
||||
STR_1878 :{WINDOW_COLOUR_2}Ellenőrzés:
|
||||
STR_1879 :Minden 10. percben
|
||||
STR_1880 :Minden 20. percben
|
||||
STR_1881 :Minden 30. percben
|
||||
STR_1882 :Minden 45. percben
|
||||
STR_1883 :Minden órában
|
||||
STR_1884 :Minden második órában
|
||||
STR_1879 :10 percenként
|
||||
STR_1880 :20 percenként
|
||||
STR_1881 :30 percenként
|
||||
STR_1882 :45 percenként
|
||||
STR_1883 :Óránként
|
||||
STR_1884 :Kétóránként
|
||||
STR_1885 :Soha
|
||||
STR_1886 :Ezt ellenőrzi: {STRINGID}
|
||||
STR_1887 :{WINDOW_COLOUR_2}Legutóbbi ellenőrzés óta eltelt idő: {BLACK}{COMMA16} perc
|
||||
|
@ -1279,9 +1279,9 @@ STR_1900 :{WINDOW_COLOUR_2}Tereprendezés
|
|||
STR_1901 :{WINDOW_COLOUR_2}Park belépőjegyek
|
||||
STR_1902 :{WINDOW_COLOUR_2}Játék jegyek
|
||||
STR_1903 :{WINDOW_COLOUR_2}Bolti eladások
|
||||
STR_1904 :{WINDOW_COLOUR_2}Bolti készletek
|
||||
STR_1904 :{WINDOW_COLOUR_2}Bolti árufeltöltés
|
||||
STR_1905 :{WINDOW_COLOUR_2}Étel/ital eladások
|
||||
STR_1906 :{WINDOW_COLOUR_2}Étel/ital készletek
|
||||
STR_1906 :{WINDOW_COLOUR_2}Étel/ital árufeltöltés
|
||||
STR_1907 :{WINDOW_COLOUR_2}Alkalmazottak bére
|
||||
STR_1908 :{WINDOW_COLOUR_2}Marketing
|
||||
STR_1909 :{WINDOW_COLOUR_2}Kutatás
|
||||
|
@ -1652,7 +1652,7 @@ STR_2291 :Válassz pályát az új játékhoz
|
|||
STR_2292 :{WINDOW_COLOUR_2}Az alábbi játékokon volt:
|
||||
STR_2293 :{BLACK} Semmi
|
||||
STR_2294 :A talajtípus megváltoztatása
|
||||
STR_2295 :A föld függőleges széleinek megváltoztatása
|
||||
STR_2295 :A talaj függőleges széleinek megváltoztatása
|
||||
STR_2296 :{BLACK}{CURRENCY2DP}{WINDOW_COLOUR_2} kiadás {BLACK}park belépőre
|
||||
STR_2297 :{BLACK}{CURRENCY2DP}{WINDOW_COLOUR_2} kiadás {BLACK}{COMMA16} játékra
|
||||
STR_2298 :{BLACK}{CURRENCY2DP}{WINDOW_COLOUR_2} kiadás {BLACK}{COMMA16} játékra
|
||||
|
@ -1737,19 +1737,19 @@ STR_2382 :Föld
|
|||
STR_2383 :Víz
|
||||
STR_2384 :{WINDOW_COLOUR_2}A célod:
|
||||
STR_2385 :{BLACK}Nincs
|
||||
STR_2386 :{BLACK}Legyen legalább {COMMA16} vendég a parkodban {MONTHYEAR}végére, a parkod értékelése pedig legyen legalább 600
|
||||
STR_2386 :{BLACK}Legyen legalább {COMMA32} vendég a parkodban {MONTHYEAR}végére, a parkod értékelése pedig legyen legalább 600
|
||||
STR_2387 :{BLACK}Legyen legalább {POP16}{POP16}{CURRENCY} a parkod értéke {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}végére
|
||||
STR_2388 :{BLACK}Érezd jól magad!
|
||||
STR_2389 :{BLACK}A lehető legjobb {STRINGID} építése!
|
||||
STR_2390 :{BLACK}Építs 10 különböző, egyenként legalább 6,00 izgalmi értékű hullámvasutat a parkodban
|
||||
STR_2391 :{BLACK}Legyen legalább {COMMA16} vendég a parkodban. A park értékelése soha nem eshet 700 alá!
|
||||
STR_2391 :{BLACK}Legyen legalább {COMMA32} vendég a parkodban. A park értékelése soha nem eshet 700 alá!
|
||||
STR_2392 :{BLACK}A játékok belépési díja utáni havi bevételed legyen legalább {POP16}{POP16}{CURRENCY}
|
||||
STR_2393 :{BLACK}Működjön 10 különböző, egyenként legalább {LENGTH} hosszú hullámvasút a parkodban, amelyek izgalmi értéke legalább 7,00
|
||||
STR_2394 :{BLACK}Fejezd be az 5 félkész hullámvasút építését, úgy, hogy mindegyik izgalmi értéke legalább {POP16}{POP16}{COMMA2DP32} legyen
|
||||
STR_2395 :{BLACK}Fizesd vissza a kölcsönödet és a park értéke legyen legalább {POP16}{POP16}{CURRENCY}
|
||||
STR_2396 :{BLACK}Az ételek, italok és szuvenírek utáni havi bevételed legyen legalább {POP16}{POP16}{CURRENCY}
|
||||
STR_2397 :Nincs
|
||||
STR_2398 :A vendégek száma
|
||||
STR_2398 :Vendégek száma
|
||||
STR_2399 :A park értéke egy meghatározott időpontban
|
||||
STR_2400 :Érezd jól magad
|
||||
STR_2401 :Építsd meg a lehető legjobb játékot
|
||||
|
@ -1850,7 +1850,7 @@ STR_2507 :Láthatatlan emberek be/ki
|
|||
STR_2508 :Magasságjelek a talajon be/ki
|
||||
STR_2509 :Magasságjelek a játékok pályáin be/ki
|
||||
STR_2510 :Magasságjelek az utakon be/ki
|
||||
STR_2511 :Föld módosítása
|
||||
STR_2511 :Talaj módosítása
|
||||
STR_2512 :Víz módosítása
|
||||
STR_2513 :Díszlet építése
|
||||
STR_2514 :Utak építése
|
||||
|
@ -1921,11 +1921,11 @@ STR_2694 :Generálás
|
|||
STR_2695 :Véletlenszerű táj
|
||||
STR_2696 :Rakjon le fákat
|
||||
STR_2700 :Automatikus mentés:
|
||||
STR_2701 :Minden percben
|
||||
STR_2702 :Minden 5. percben
|
||||
STR_2703 :Minden 15. percben
|
||||
STR_2704 :Minden 30. percben
|
||||
STR_2705 :Minden órában
|
||||
STR_2701 :Percenként
|
||||
STR_2702 :5 percenként
|
||||
STR_2703 :15 percenként
|
||||
STR_2704 :30 percenként
|
||||
STR_2705 :Óránként
|
||||
STR_2706 :Soha
|
||||
STR_2707 :Rendszer fájlkezelőjének használata
|
||||
STR_2708 :{WINDOW_COLOUR_1}Biztos, hogy felül akarod írni ezt: {STRINGID}?
|
||||
|
@ -1947,7 +1947,7 @@ STR_2730 :{UINT16} óra {UINT16} perc
|
|||
STR_2731 :{UINT16} óra {UINT16} perc
|
||||
STR_2732 :{COMMA32} láb
|
||||
STR_2733 :{COMMA32} m
|
||||
STR_2734 :{COMMA16} mph
|
||||
STR_2734 :{COMMA16} mi/h
|
||||
STR_2735 :{COMMA16} km/h
|
||||
STR_2736 :{POP16}{COMMA16}. év {PUSH16}{PUSH16}{MONTH}
|
||||
STR_2737 :{POP16}{POP16}{COMMA16}. év {PUSH16}{PUSH16}{MONTH} {PUSH16}{PUSH16}{STRINGID}
|
||||
|
@ -1988,9 +1988,9 @@ STR_2786 :Kattints a parancs leírására új gyorsbillentyű megadásához
|
|||
STR_2787 :{WINDOW_COLOUR_2}A park értéke: {BLACK}{CURRENCY}
|
||||
STR_2788 :{WINDOW_COLOUR_2}Gratulálunk !{NEWLINE}{BLACK}Sikeresen teljesítetted a kitűzött célt egy {CURRENCY} értékű vállalattal !
|
||||
STR_2789 :{WINDOW_COLOUR_2}Nem sikerült teljesítened a kitűzött célt !
|
||||
STR_2790 :Írd be a neved az eredménytáblába
|
||||
STR_2791 :Írd be a neved
|
||||
STR_2792 :Kérlek írd be a neved az eredménytáblába:
|
||||
STR_2790 :Név beírása az eredménytáblába
|
||||
STR_2791 :Név beírása
|
||||
STR_2792 :Írd be a neved az eredménytáblába:
|
||||
STR_2793 :(Teljesítette: {STRINGID})
|
||||
STR_2794 :{BLACK}{STRINGID} {WINDOW_COLOUR_2}teljesítette{NEWLINE}{WINDOW_COLOUR_2}egy {BLACK}{CURRENCY} {WINDOW_COLOUR_2}értékű vállalattal
|
||||
STR_2795 :Rendezés
|
||||
|
@ -2169,7 +2169,7 @@ STR_3130 :Mentés
|
|||
STR_3131 :Mégse
|
||||
STR_3132 :{BLACK}Klikkelj azokra a díszletelemekre, amelyeket a játéktervvel együtt el akarsz menteni…
|
||||
STR_3133 :Nem építhető lejtőre
|
||||
STR_3134 :{RED}(A terv el nem érhető díszletet tartalmaz)
|
||||
STR_3134 :{RED}(A terv olyan díszletet tartalmaz, amely nem elérhető)
|
||||
STR_3135 :{RED}(A járműterv nem elérhető - Befolyásolhatja a játék teljesítményét)
|
||||
STR_3136 :Figyelmeztetés: A terv alternatív járműtípussal lesz megépítve és lehet, hogy nem az elvártnak megfelelően fog teljesíteni
|
||||
STR_3137 :Közeli díszletek kiválasztása
|
||||
|
@ -2196,7 +2196,7 @@ STR_3176 :Az objektum nem választható ki
|
|||
STR_3177 :Az objektum kiválasztása nem vonható vissza
|
||||
STR_3179 :Legalább egy játék járművet/játékot ki kell választani
|
||||
STR_3180 :Érvénytelen objektumkijelölés
|
||||
STR_3181 :Objektum kiválasztás - {STRINGID}
|
||||
STR_3181 :Objektumok kiválasztása - {STRINGID}
|
||||
STR_3182 :A parkbejárat típusát ki kell választani
|
||||
STR_3183 :A víz típusát ki kell választani
|
||||
STR_3184 :Játékok és járműveik
|
||||
|
@ -2253,7 +2253,7 @@ STR_3235 :Pénzügyi beállítások
|
|||
STR_3236 :A vendégek beállításai
|
||||
STR_3237 :A park beállításai
|
||||
STR_3238 :Nem kell pénz
|
||||
STR_3239 :Legyen ez a park egy pénzügyi korlátok nélküli, ’nem kell pénz’ park
|
||||
STR_3239 :Legyen ez a park egy „pénz nélküli” park, pénzügyi korlátozások nélkül
|
||||
STR_3240 :{WINDOW_COLOUR_2}Kezdőtőke:
|
||||
STR_3241 :{WINDOW_COLOUR_2}Kezdeti kölcsön:
|
||||
STR_3242 :{WINDOW_COLOUR_2}Maximális kölcsönösszeg:
|
||||
|
@ -2308,7 +2308,7 @@ STR_3290 :Hűvös és nedves
|
|||
STR_3291 :Meleg
|
||||
STR_3292 :Forró és száraz
|
||||
STR_3293 :Hideg
|
||||
STR_3294 :Változtatás…
|
||||
STR_3294 :Változtat…
|
||||
STR_3295 :A park nevének megváltoztatása
|
||||
STR_3296 :A pálya nevének megváltoztatása
|
||||
STR_3297 :A park / pálya részletes leírásának megváltoztatása
|
||||
|
@ -2323,7 +2323,7 @@ STR_3305 :{WINDOW_COLOUR_2}Havi bevétel:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Havi nyereség:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Minimum hosszúság:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Izgalmi érték:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Műemlékvédelmi játékok/épületek:
|
||||
|
@ -2392,7 +2392,7 @@ STR_3380 :A játékterv nem telepíthető…
|
|||
STR_3381 :A fájl nem kompatibilis, vagy érvénytelen adatokat tartalmaz
|
||||
STR_3382 :A fájl másolása meghiúsult
|
||||
STR_3383 :Válassz egy új nevet a játékterv számára
|
||||
STR_3384 :Már létezik egy játékterv ezen a néven - Kérlek válassz egy új nevet a terv számára:
|
||||
STR_3384 :Már létezik egy játékterv ezen a néven - Válassz egy új nevet a terv számára:
|
||||
STR_3389 :Nem választható ki több díszletelem…
|
||||
STR_3390 :Túl sok elem van kiválasztva
|
||||
STR_3437 :Nagy földterületek megtisztítása a díszletektől
|
||||
|
@ -2408,7 +2408,7 @@ STR_5122 :A játékok pályatípus szerinti kiválasztása (mint az RCT1-ben)
|
|||
STR_5123 :Játékok felújítása
|
||||
STR_5125 :Minden rombolható
|
||||
STR_5126 :Véletlenszerű főcímzene
|
||||
STR_5127 :Húzás közben tájképfestés a magasságváltoztatás helyett
|
||||
STR_5127 :Húzás közben a táj festése, magasságváltoztatás helyett
|
||||
STR_5128 :Kiválasztás mérete
|
||||
STR_5129 :Add meg a kiválasztás méretét {COMMA16} és {COMMA16} között
|
||||
STR_5130 :Térkép mérete
|
||||
|
@ -2460,7 +2460,7 @@ STR_5189 :Kutatás
|
|||
STR_5190 :Térkép
|
||||
STR_5191 :Látkép
|
||||
STR_5192 :Friss hírek
|
||||
STR_5193 :Föld
|
||||
STR_5193 :Talaj
|
||||
STR_5194 :Víz
|
||||
STR_5195 :Díszlet törlése
|
||||
STR_5196 :Földjogok
|
||||
|
@ -2477,7 +2477,7 @@ STR_5206 :Vendéglista
|
|||
STR_5207 :Személyzet
|
||||
STR_5208 :Személyzeti lista
|
||||
STR_5209 :Hirdetőtábla
|
||||
STR_5210 :Objektum választás
|
||||
STR_5210 :Objektumok kiválasztása
|
||||
STR_5211 :Találmányok listája
|
||||
STR_5212 :Pályabeállítások
|
||||
STR_5213 :Cél beállítások
|
||||
|
@ -2609,7 +2609,7 @@ STR_5365 :{BLACK}Dolgozók sebessége:
|
|||
STR_5366 :Normál
|
||||
STR_5367 :Gyors
|
||||
STR_5368 :Ütközés törlése
|
||||
STR_5371 :Objektum választás
|
||||
STR_5371 :Objektumok kiválasztása
|
||||
STR_5372 :Jobb egérgombos húzás invertálása
|
||||
STR_5373 :Név {STRINGID}
|
||||
STR_5374 :Dátum {STRINGID}
|
||||
|
@ -2717,7 +2717,7 @@ STR_5547 :Világos rózsaszín
|
|||
STR_5548 :Minden működési mód megjelenítése
|
||||
STR_5549 :Év/hónap/nap
|
||||
STR_5550 :{POP16}{POP16}{COMMA16}. év {PUSH16}{PUSH16}{MONTH} {PUSH16}{PUSH16}{STRINGID}
|
||||
STR_5551 :év/nap/hónap
|
||||
STR_5551 :Év/nap/hónap
|
||||
STR_5552 :{POP16}{POP16}{COMMA16}. év {PUSH16}{PUSH16}{PUSH16}{STRINGID} {MONTH}
|
||||
STR_5553 :Játék megállítása, ha a Steam Átfedés nyitva van
|
||||
STR_5554 :A hegy-eszköz bekapcsolása
|
||||
|
@ -2726,7 +2726,7 @@ STR_5556 :Játékos kirúgása
|
|||
STR_5557 :Kapcsolat fenntartása hiba esetén (többjátékos mód)
|
||||
STR_5558 :A beállítás érvénybe lépéséhez az OpenRCT2 újraindítása szükséges
|
||||
STR_5559 :10 perces ellenőrzés
|
||||
STR_5560 :Az ellenőrzési időt „minden 10. percben”-re állítja az összes játékon
|
||||
STR_5560 :Az ellenőrzési időt „10 percenként”-re állítja az összes játékon
|
||||
STR_5561 :A nyelv betöltése sikertelen
|
||||
STR_5562 :FIGYELMEZTETÉS!
|
||||
STR_5563 :Ez a funkció jelenleg instabil, legyél rendkívül elővigyázatos.
|
||||
|
@ -2800,7 +2800,7 @@ STR_5633 :CMD +
|
|||
STR_5634 :OPTION +
|
||||
STR_5635 :{WINDOW_COLOUR_2}Elköltött pénz: {BLACK}{CURRENCY2DP}
|
||||
STR_5636 :{WINDOW_COLOUR_2}Futtatott parancsok: {BLACK}{COMMA16}
|
||||
STR_5637 :Ezt nem csinálhatod…
|
||||
STR_5637 :Nem lehet megtenni…
|
||||
STR_5638 :Engedély megtagadva
|
||||
STR_5639 :Játékosok listája
|
||||
STR_5640 :Csoportok kezelése
|
||||
|
@ -3053,7 +3053,7 @@ STR_5930 :Nagy díszlet részletei
|
|||
STR_5931 :Hirdetőtábla részletei
|
||||
STR_5932 :Sérült elem részletei
|
||||
STR_5933 :Tulajdonságok
|
||||
STR_5934 :Terep textúrája: {BLACK}{STRINGID}
|
||||
STR_5934 :Talaj textúrája: {BLACK}{STRINGID}
|
||||
STR_5935 :Terepszegély: {BLACK}{STRINGID}
|
||||
STR_5936 :Földtulajdon: {BLACK}{STRINGID}
|
||||
STR_5937 :Nincs tulajdonban és nem eladó
|
||||
|
@ -3154,7 +3154,7 @@ STR_6031 :Szerver leírása:
|
|||
STR_6032 :Szerver üdvözlés:
|
||||
STR_6033 :Az RCT1 telepítési útvonala:
|
||||
STR_6034 :{BLACK}{STRING}
|
||||
STR_6035 :Kérlek válaszd ki az RCT1 mappádat
|
||||
STR_6035 :Válaszd ki az RCT1 mappádat
|
||||
STR_6036 :Törlés
|
||||
STR_6037 :A kiválasztott könyvtár nem tartalmaz érvényes RollerCoaster Tycoon 1 telepítést.
|
||||
STR_6038 :Ha az RCT1 telepítve van, itt add meg a mappája helyét, hogy betölthesd a pályákat, zenét stb.
|
||||
|
@ -3162,7 +3162,7 @@ STR_6039 :Játék/épület gyors lerombolása
|
|||
STR_6040 :Pályabeállítások módosítása
|
||||
STR_6041 :{BLACK}Nem vettél fel gépészt!
|
||||
STR_6042 :Magasságtérkép betöltése
|
||||
STR_6043 :Magasságtérkép kiválasztása
|
||||
STR_6043 :Magasságtérkép választása
|
||||
STR_6044 :Magasságtérkép simítása
|
||||
STR_6045 :Erősség
|
||||
STR_6046 :Magasságtérkép normalizálása
|
||||
|
@ -3243,7 +3243,7 @@ STR_6129 :Másolás
|
|||
STR_6130 :Összes másolása
|
||||
STR_6131 :Objektum forrása
|
||||
STR_6132 :Kutatási állapot figyelmen kívül hagyása
|
||||
STR_6133 :Hozzáférés a még fel nem fedezett játékokhoz és díszletekhez
|
||||
STR_6133 :Hozzáférés a még felfedezetlen játékokhoz és díszletekhez
|
||||
STR_6134 :Díszlet törlése
|
||||
STR_6135 :A kliens érvénytelen kérést küldött
|
||||
STR_6136 :A szerver érvénytelen kérést küldött
|
||||
|
@ -3265,7 +3265,7 @@ STR_6151 :A főszerver nem tudja listázni a szervereket
|
|||
STR_6152 :Érvénytelen válasz a főszervertől (nincs JSON-tömb)
|
||||
STR_6153 :Fizetős parkbelépő / Fizetős játékok
|
||||
STR_6154 :Biztonsági okokból nem javasolt az OpenRCT2 emelt jogosultságokkal történő futtatása.
|
||||
STR_6155 :Se a KDialog, se a Zenity nincs telepítve. Kérlek telepítsd valamelyiket, vagy konfiguráld a parancssorból.
|
||||
STR_6155 :Se a KDialog, se a Zenity nincs telepítve. Telepítsd valamelyiket, vagy konfiguráld a parancssorból.
|
||||
STR_6156 :A név le van foglalva
|
||||
STR_6157 :Konzol
|
||||
STR_6160 :{WINDOW_COLOUR_2}Rendelkezésre álló járművek: {BLACK}{STRING}
|
||||
|
@ -3281,7 +3281,7 @@ STR_6169 :Pályaválasztás
|
|||
STR_6170 :Felület finomhangolása
|
||||
STR_6171 :Keresés
|
||||
STR_6172 :Keresés
|
||||
STR_6173 :Kérlek add meg a keresendő nevet:
|
||||
STR_6173 :Add meg a keresendő nevet:
|
||||
STR_6188 :Hányás
|
||||
STR_6189 :Kacsa
|
||||
STR_6191 :Felület
|
||||
|
@ -3349,7 +3349,7 @@ STR_6266 :Egyedi tartalmak mappájának megnyitása
|
|||
STR_6267 :Mezővizsgáló megnyitása
|
||||
STR_6268 :Megállított játék léptetése
|
||||
STR_6269 :Érvénytelen éghajlati ID
|
||||
STR_6270 :Terepfelületek
|
||||
STR_6270 :Talajfelületek
|
||||
STR_6271 :Terepszegélyek
|
||||
STR_6272 :Állomások
|
||||
STR_6273 :Zene
|
||||
|
@ -3419,7 +3419,7 @@ STR_6346 :Mezővizsgáló: Elem magasságának csökkentése
|
|||
STR_6347 :Úti extrák nem helyezhetőek útátjárókra!
|
||||
STR_6348 :Előbb távolítsd el az útátjárót!
|
||||
STR_6349 :Véletlenszerű főcím
|
||||
STR_6350 :Szórás
|
||||
STR_6350 :Szétszórás
|
||||
STR_6351 :Díszletszóró eszköz
|
||||
STR_6352 :Sűrűség
|
||||
STR_6353 :Alacsony sűrűség
|
||||
|
@ -3459,25 +3459,25 @@ STR_6386 :Hóvihar
|
|||
STR_6387 :Nem süllyeszthető itt le az elem…
|
||||
STR_6388 :Nem emelhető itt meg az elem…
|
||||
STR_6389 :Érvénytelen űrszelvény
|
||||
STR_6390 :Az OpenRCT2 működéséhez szükség van az eredeti RollerCoaster Tycoon 2 fájljaira. Kérlek add meg a mappát, ahova telepítetted a RollerCoaster Tycoon 2-t.
|
||||
STR_6391 :Kérlek válaszd ki az RCT2 mappádat
|
||||
STR_6390 :Az OpenRCT2 működéséhez szükség van az eredeti RollerCoaster Tycoon 2 fájljaira. Add meg a mappát, ahova telepítetted a RollerCoaster Tycoon 2-t.
|
||||
STR_6391 :Válaszd ki az RCT2 mappádat
|
||||
STR_6392 :A(z) {STRING} nem található ezen az útvonalon.
|
||||
STR_6393 :Cél kiválasztása
|
||||
STR_6394 :Cél
|
||||
STR_6395 :Karbantartás
|
||||
STR_6396 :Képernyővédő és a monitor energiagazdálkodása ki
|
||||
STR_6397 :Ha ki van választva, szüneteltetve lesz a képernyővédő és a monitor egyéb energiagazdálkodási funkciói, amíg az OpenRCT2 fut.
|
||||
STR_6398 :A fájl nem támogatott játék-típusokat tartalmaz. Kérlek frissíts egy újabb OpenRCT2 verzióra.
|
||||
STR_6399 :Az OpenRCT2 működéséhez szükség van az eredeti RollerCoaster Tycoon 2 fájljaira. Kérlek állítsd be a „game_path” változót a config.ini-ben arra a mappára, amelybe telepítetted a RollerCoaster Tycoon 2-t, majd indítsd újra az OpenRCT2-t.
|
||||
STR_6398 :A fájl nem támogatott játék-típusokat tartalmaz. Frissíts egy újabb OpenRCT2 verzióra.
|
||||
STR_6399 :Az OpenRCT2 működéséhez szükség van az eredeti RollerCoaster Tycoon 2 fájljaira. Állítsd be a „game_path” változót a config.ini-ben arra a mappára, amelybe telepítetted a RollerCoaster Tycoon 2-t, majd indítsd újra az OpenRCT2-t.
|
||||
STR_6400 :Letöltöttem a GOG RollerCoaster Tycoon 2 offline telepítőjét, de nincs telepítve
|
||||
STR_6401 :Már telepítettem a RollerCoaster Tycoon 2-t
|
||||
STR_6402 :OpenRCT2 adatok megadása
|
||||
STR_6403 :Válaszd ki a számodra legmegfelelőbbet
|
||||
STR_6404 :Kérlek válaszd ki a GOG RollerCoaster Tycoon 2 telepítőjét.
|
||||
STR_6404 :Válaszd ki a GOG RollerCoaster Tycoon 2 telepítőjét.
|
||||
STR_6405 :GOG telepítő kiválasztása
|
||||
STR_6406 :GOG RollerCoaster Tycoon 2 telepítő
|
||||
STR_6407 :Ez eltarthat pár percig.
|
||||
STR_6408 :Kérlek rakd fel az “innoextract”-ot a GOG telepítő kitömörítéséhez, majd indítsd újra az OpenRCT2-t.
|
||||
STR_6408 :Rakd fel az „innoextract”-ot a GOG telepítő kitömörítéséhez, majd indítsd újra az OpenRCT2-t.
|
||||
STR_6409 :A kiválasztott fájl nem a GOG RollerCoaster Tycoon 2 offline telepítője. Lehet, hogy a GOG Galaxy letöltő stub-ját töltötted le, vagy rossz fájlt választottál.
|
||||
STR_6410 :Kicsinyítés/nagyítás
|
||||
STR_6411 :A kicsinyítés és nagyítás gombjainak megjelenítése az eszköztáron
|
||||
|
@ -3521,7 +3521,7 @@ STR_6448 :Nem támogatott objektumformátum
|
|||
STR_6449 :{WINDOW_COLOUR_2}Számok:
|
||||
STR_6450 :{BLACK}„{STRING}”
|
||||
STR_6451 :{BLACK}„{STRING}” - {STRING}
|
||||
STR_6452 :{WINDOW_COLOUR_2}Árul: {BLACK}{STRING}
|
||||
STR_6452 :{WINDOW_COLOUR_2}Ezeket árulja: {BLACK}{STRING}
|
||||
STR_6453 :Verzió infó másolása
|
||||
STR_6454 :A hirdetőtábla nem nevezhető át…
|
||||
STR_6455 :A felirat nem nevezhető át…
|
||||
|
@ -3572,7 +3572,7 @@ STR_6500 :A játékterv formátum által nem támogatott pályaelemek
|
|||
STR_6501 :Véletlenszerű szín
|
||||
STR_6502 :Írj be egy értéket {COMMA16} és {COMMA16} között
|
||||
STR_6503 :Legalább egy állomásobjektumot ki kell választani
|
||||
STR_6504 :Legalább egy terepfelületet ki kell választani
|
||||
STR_6504 :Legalább egy talajfelületet ki kell választani
|
||||
STR_6505 :Legalább egy terepszegélyt ki kell választani
|
||||
STR_6506 :Nagy fél dugóhúzó (balra)
|
||||
STR_6507 :Nagy fél dugóhúzó (jobbra)
|
||||
|
@ -3686,6 +3686,11 @@ STR_6614 :Nem változtatható meg a park belépési díja
|
|||
STR_6615 :A pályának ezen a mezőn vízre van szüksége
|
||||
STR_6616 :Az adott személyzettípus számára érvénytelen akció
|
||||
STR_6617 :Nem lehet önmagával cserélni mezőelemet
|
||||
STR_6618 :Nem lehet korlátozni vagy feloldani az objektumot...
|
||||
STR_6619 :Az objektumtípus nem korlátozható!
|
||||
STR_6620 :Az objektum nem található!
|
||||
STR_6621 :Korlátozás
|
||||
STR_6622 :Objektum korlátozása a pályaszerkesztőre és a homokozó módra.
|
||||
|
||||
#############
|
||||
# Scenarios #
|
||||
|
@ -3713,9 +3718,9 @@ STR_PARK :Gyémánt-fennsík
|
|||
STR_DTLS :Gyémánt-fennsík már egy sikeres vidámpark, remek játékokkal - fejleszd, hogy megduplázd az értékét
|
||||
|
||||
<Evergreen Gardens>
|
||||
STR_SCNR :Örökzöld Kertek
|
||||
STR_PARK :Örökzöld Kertek
|
||||
STR_DTLS :Alakítsd át a csodaszép Örökzöld Kerteket egy virágzó vidámparkká
|
||||
STR_SCNR :Örökzöld Kert
|
||||
STR_PARK :Örökzöld Kert
|
||||
STR_DTLS :Alakítsd át a csodaszép Örökzöld Kertet egy virágzó vidámparkká
|
||||
|
||||
<Bumbly Beach>
|
||||
STR_SCNR :Suta Strand
|
||||
|
@ -3949,8 +3954,8 @@ STR_PARK :Városi Park
|
|||
STR_DTLS :Egy apró park egyezséget kötött a közeli várossal, amelynek értelmében magán a városon keresztül terjeszkedhet
|
||||
|
||||
<Geoffrey Gardens>
|
||||
STR_SCNR :Geoffrey Kertek
|
||||
STR_PARK :Geoffrey Kertek
|
||||
STR_SCNR :Geoffrey Kert
|
||||
STR_PARK :Geoffrey Kert
|
||||
STR_DTLS :Egy nagy kertnek arra van szüksége, hogy sikeres vidámpark legyen belőle
|
||||
|
||||
|
||||
|
@ -4218,9 +4223,9 @@ STR_PARK :Szellemváros
|
|||
STR_DTLS :Egy nagy vidámparklánc felbérelt, hogy építs nekik óriási hullámvasútparkot egy elhagyatott bányászváros köré
|
||||
|
||||
<Gravity Gardens>
|
||||
STR_SCNR :Gravitációs Kertek
|
||||
STR_PARK :Gravitációs Kertek
|
||||
STR_DTLS :A kihívásod az, hogy építs egy hullámvasútparkot a gyönyörű Gravitációs Kertekben. Semmi más játékot, csak hullámvasutakat!
|
||||
STR_SCNR :Gravitációs Kert
|
||||
STR_PARK :Gravitációs Kert
|
||||
STR_DTLS :A kihívásod az, hogy építs egy hullámvasútparkot a gyönyörű Gravitációs Kertben. Semmi más játékot, csak hullámvasutakat!
|
||||
|
||||
<Infernal Views>
|
||||
STR_SCNR :Pokoli Kilátások
|
||||
|
|
|
@ -1740,12 +1740,12 @@ STR_2382 :Terreno
|
|||
STR_2383 :Acqua
|
||||
STR_2384 :{WINDOW_COLOUR_2}Il tuo obiettivo:
|
||||
STR_2385 :{BLACK}Nessuno
|
||||
STR_2386 :{BLACK}Avere almeno {COMMA16} visitatori nel parco entro la fine di {MONTHYEAR}, con una valutazione del parco di almeno 600
|
||||
STR_2386 :{BLACK}Avere almeno {COMMA32} visitatori nel parco entro la fine di {MONTHYEAR}, con una valutazione del parco di almeno 600
|
||||
STR_2387 :{BLACK}Far raggiungere al parco un valore di almeno {POP16}{POP16}{CURRENCY} entro la fine di {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}
|
||||
STR_2388 :{BLACK}Divertirsi!
|
||||
STR_2389 :{BLACK}Costruire il migliore {STRINGID} si possa!
|
||||
STR_2390 :{BLACK}Avere 10 tipi diversi di ottovolante nel parco, ognuno con un livello di divertimento di almeno 6.00
|
||||
STR_2391 :{BLACK}Avere almeno {COMMA16} visitatori nel parco. E non devi mai avere una valutazione del parco inferiore a 700!
|
||||
STR_2391 :{BLACK}Avere almeno {COMMA32} visitatori nel parco. E non devi mai avere una valutazione del parco inferiore a 700!
|
||||
STR_2392 :{BLACK}Incassare mensilmente tramite i biglietti delle attrazioni almeno {POP16}{POP16}{CURRENCY}
|
||||
STR_2393 :{BLACK}Avere 10 tipi diversi di ottovolante nel parco, ognuno della lunghezza minima di {LENGTH} e un livello di divertimento di almeno 7.00
|
||||
STR_2394 :{BLACK}Finire di costruire tutti e cinque gli ottovolanti parzialmente costruiti in questo parco, facendo loro raggiungere un livello di divertimento di almeno {POP16}{POP16}{COMMA2DP32} ciascuno
|
||||
|
@ -2325,7 +2325,7 @@ STR_3305 :{WINDOW_COLOUR_2}Entrate mensili:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Profitto mensile:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Lunghezza minima:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Liv. divertimento:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Attrazioni sotto ordine di protezione:
|
||||
|
|
|
@ -1729,12 +1729,12 @@ STR_2382 :地形
|
|||
STR_2383 :水
|
||||
STR_2384 :{WINDOW_COLOUR_2}あなたの目標:
|
||||
STR_2385 :{BLACK}なし
|
||||
STR_2386 :{BLACK}{POP16}{MONTHYEAR}の終わりに、 パークの内に{PUSH16}{PUSH16}{COMMA16}以上人の来客し、600以上のパーク評価値を取得する必要があります。
|
||||
STR_2386 :{BLACK}{POP16}{MONTHYEAR}の終わりに、 パークの内に{PUSH16}{PUSH16}{COMMA32}以上人の来客し、600以上のパーク評価値を取得する必要があります。
|
||||
STR_2387 :{BLACK}{POP16}{MONTHYEAR}の終わりに、 {CURRENCY}以上のパーク評価額を取得する必要があります。
|
||||
STR_2388 :{BLACK}楽しんで下さい!
|
||||
STR_2389 :{BLACK}最高の{STRINGID}を建設してください!
|
||||
STR_2390 :{BLACK}パークで10タイプのジェットコースターを稼働させるには、それぞれ少なくとも6.00以上の興奮度が必要です。
|
||||
STR_2391 :{BLACK}パークに少なくとも{COMMA16}ゲストを入れてを取得する必要があります。パークの評価額がいつでも700以下にならないようにしてはいけません!
|
||||
STR_2391 :{BLACK}パークに少なくとも{COMMA32}ゲストを入れてを取得する必要があります。パークの評価額がいつでも700以下にならないようにしてはいけません!
|
||||
STR_2392 :{BLACK}ライドの月次総収入の{POP16}{POP16}{CURRENCY}を取得する必要があります。
|
||||
STR_2393 :{BLACK}パークで10タイプのジェットコースターを操作しますし、それぞれ所定の長さの{LENGTH}し, 7.00以上の興奮度が必要です。
|
||||
STR_2394 :{BLACK}パークであるの5ジェットコースターの建設を仕上げますし、 それぞれ{POP16}{POP16}{COMMA2DP32}以上の興奮度が必要です。
|
||||
|
@ -2315,7 +2315,7 @@ STR_3305 :{WINDOW_COLOUR_2}月売上:
|
|||
STR_3306 :{WINDOW_COLOUR_2}月利益:
|
||||
STR_3307 :{WINDOW_COLOUR_2}最小長:
|
||||
STR_3308 :{WINDOW_COLOUR_2}興奮度:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}保全命令のライド:
|
||||
|
|
|
@ -1741,12 +1741,12 @@ STR_2382 :땅
|
|||
STR_2383 :물
|
||||
STR_2384 :{WINDOW_COLOUR_2}목표:
|
||||
STR_2385 :{BLACK}없음
|
||||
STR_2386 :{BLACK}공원에 최소 {COMMA16}명 이상의 손님을 {MONTHYEAR}까지 유치하고, 공원 등급을 600 이상으로 유지하세요
|
||||
STR_2386 :{BLACK}공원에 최소 {COMMA32}명 이상의 손님을 {MONTHYEAR}까지 유치하고, 공원 등급을 600 이상으로 유지하세요
|
||||
STR_2387 :{BLACK}최소 {POP16}{POP16}{CURRENCY} 이상의 공원 가치를 {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}까지 달성하세요
|
||||
STR_2388 :{BLACK}즐기세요!
|
||||
STR_2389 :{BLACK}최고의 {STRINGID}(을)를 건설하세요!
|
||||
STR_2390 :{BLACK}흥미도가 최소 6.00을 넘는 10종의 다른 롤러코스터를 공원에 운용하세요
|
||||
STR_2391 :{BLACK}공원에 최소 {COMMA16}명 이상의 손님을 유치하세요. 단 한 순간이라도 공원 등급을 700 아래로 떨어뜨려서는 안 됩니다!
|
||||
STR_2391 :{BLACK}공원에 최소 {COMMA32}명 이상의 손님을 유치하세요. 단 한 순간이라도 공원 등급을 700 아래로 떨어뜨려서는 안 됩니다!
|
||||
STR_2392 :{BLACK}놀이기구 탑승 수익만으로 월 수익을 최소 {POP16}{POP16}{CURRENCY} 이상 달성하세요
|
||||
STR_2393 :{BLACK}최소 길이가 {LENGTH} 이상이고 흥미도가 최소 7.00을 넘는 10개의 다른 종류의 롤러코스터를 공원에 운용하세요
|
||||
STR_2394 :{BLACK}이 공원에 미완성된 5대의 롤러코스터를 각각의 흥미도가 최소 {POP16}{POP16}{COMMA2DP32} 이상이 되도록 완성하세요
|
||||
|
@ -2327,7 +2327,7 @@ STR_3305 :{WINDOW_COLOUR_2}월 수익:
|
|||
STR_3306 :{WINDOW_COLOUR_2}월 순이익:
|
||||
STR_3307 :{WINDOW_COLOUR_2}최소 길이:
|
||||
STR_3308 :{WINDOW_COLOUR_2}흥미도:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}수정·파괴할 수 없는 놀이기구/시설:
|
||||
|
@ -2585,7 +2585,7 @@ STR_5335 :놀이기구 입구
|
|||
STR_5336 :놀이기구 출구
|
||||
STR_5337 :공원 입구
|
||||
STR_5338 :요소 종류
|
||||
STR_5339 :기본 고도
|
||||
STR_5339 :기본 높이
|
||||
STR_5340 :최상단 높이
|
||||
STR_5343 :직원 자동 배치
|
||||
STR_5344 :변경기록
|
||||
|
@ -2783,9 +2783,9 @@ STR_5612 :유령 플래그
|
|||
STR_5613 :B
|
||||
STR_5614 :깨짐 플래그
|
||||
STR_5615 :L
|
||||
STR_5616 :타일 플래그의 마지막 요소
|
||||
STR_5617 :선택한 요소를 위로 이동합니다.
|
||||
STR_5618 :선택한 요소를 아래로 이동합니다.
|
||||
STR_5616 :이 칸의 마지막 요소
|
||||
STR_5617 :선택한 요소를 위로 이동합니다
|
||||
STR_5618 :선택한 요소를 아래로 이동합니다
|
||||
STR_5619 :롤러코스터 타이쿤
|
||||
STR_5620 :애디드 어트랙션
|
||||
STR_5621 :루피 랜드스케이프
|
||||
|
@ -3689,6 +3689,11 @@ STR_6614 :공원 입장료를 변경할 수 없습니다
|
|||
STR_6615 :이 칸에 있는 트랙은 물이 있어야 합니다
|
||||
STR_6616 :그 직원에 대한 잘못된 행동입니다
|
||||
STR_6617 :칸 요소를 자기 자신과 바꿀 수는 없습니다
|
||||
STR_6618 :오브젝트를 제한하거나 제한 해제할 수 없습니다…
|
||||
STR_6619 :오브젝트 종류를 제한할 수 없습니다!
|
||||
STR_6620 :오브젝트를 찾을 수 없습니다!
|
||||
STR_6621 :제한
|
||||
STR_6622 :시나리오 에디터 및 모래상자 모드에서 선택한 오브젝트를 사용할 수 없도록 제한합니다.
|
||||
|
||||
#############
|
||||
# Scenarios #
|
||||
|
|
|
@ -1737,12 +1737,12 @@ STR_2382 :Land
|
|||
STR_2383 :Vann
|
||||
STR_2384 :{WINDOW_COLOUR_2}Ditt oppdrag:
|
||||
STR_2385 :{BLACK}Ingen
|
||||
STR_2386 :{BLACK}Å ha minst {COMMA16} gjester i din park ved slutten av {MONTHYEAR}, med en parkvurdering på minst 600
|
||||
STR_2386 :{BLACK}Å ha minst {COMMA32} gjester i din park ved slutten av {MONTHYEAR}, med en parkvurdering på minst 600
|
||||
STR_2387 :{BLACK}Å oppnå en parkverdi på minst {POP16}{POP16}{CURRENCY} ved slutten av {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}
|
||||
STR_2388 :{BLACK}Ha det Gøy!
|
||||
STR_2389 :{BLACK}Bygg den beste {STRINGID} du kan!
|
||||
STR_2390 :{BLACK}Å ha 10 forskjellige typer berg-og-dal-baner i drift i din park, hver med et spenningsnivå på minst 6.00
|
||||
STR_2391 :{BLACK}Å ha minst {COMMA16} gjester i din park. Du må ikke la parkvurderingen falle under 700 på noe tidspunkt!
|
||||
STR_2391 :{BLACK}Å ha minst {COMMA32} gjester i din park. Du må ikke la parkvurderingen falle under 700 på noe tidspunkt!
|
||||
STR_2392 :{BLACK}Å oppnå en månedlig inntekt fra attraksjons-billetter på minst {POP16}{POP16}{CURRENCY}
|
||||
STR_2393 :{BLACK}Å ha 10 forskjellige typer berg-og-dal-baner i drift i din park, hver med en minimumslengde på {LENGTH}, og med spenningsnivå på minst 7.00
|
||||
STR_2394 :{BLACK}Å bygge ferdig alle 5 halvferdige berg-og-dal-baner i denne parken og designe hver av de for å oppnå spenningsnivå på minst {POP16}{POP16}{COMMA2DP32}
|
||||
|
@ -2323,7 +2323,7 @@ STR_3305 :{WINDOW_COLOUR_2}Månedlig inntekt:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Månedlig profitt:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Minimum lengde:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Spenningsnivå:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Attraksjoner som blir bevart:
|
||||
|
@ -3687,6 +3687,11 @@ STR_6614 :Kan ikke endre inngangsavgift til parken
|
|||
STR_6615 :Spor på denne ruten trenger vann
|
||||
STR_6616 :Handling ugyldig for denne personaltypen
|
||||
STR_6617 :Kan ikke bytte ut rute-element med seg selv
|
||||
STR_6618 :Kan ikke begrense eller frigjøre objekt...
|
||||
STR_6619 :Objekt-type kan ikke bli begrenset!
|
||||
STR_6620 :Kunne ikke finne objekt!
|
||||
STR_6621 :Begrens
|
||||
STR_6622 :Begrens objekt til Scenario-redigerer og Sandkassemodus.
|
||||
|
||||
#############
|
||||
# Scenarios #
|
||||
|
|
|
@ -1738,12 +1738,12 @@ STR_2382 :Land
|
|||
STR_2383 :Water
|
||||
STR_2384 :{WINDOW_COLOUR_2}Je doelstelling:
|
||||
STR_2385 :{BLACK}Geen
|
||||
STR_2386 :{BLACK}Om ten minste {COMMA16} bezoekers in je park te hebben aan het einde van {MONTHYEAR}, met een parkwaardering van ten minste 600
|
||||
STR_2386 :{BLACK}Om ten minste {COMMA32} bezoekers in je park te hebben aan het einde van {MONTHYEAR}, met een parkwaardering van ten minste 600
|
||||
STR_2387 :{BLACK}Om een parkwaarde van ten minste {POP16}{POP16}{CURRENCY} te hebben aan het einde van {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}
|
||||
STR_2388 :{BLACK}Om plezier te hebben!
|
||||
STR_2389 :{BLACK}Om de beste {STRINGID} te bouwen!
|
||||
STR_2390 :{BLACK}Om 10 verschillende typen achtbanen in je park te hebben, elk met een plezierwaarde van ten minste 6,00
|
||||
STR_2391 :{BLACK}Om ten minste {COMMA16} bezoekers in je park te hebben. Je parkwaardering mag geen moment onder de 700 komen!
|
||||
STR_2391 :{BLACK}Om ten minste {COMMA32} bezoekers in je park te hebben. Je parkwaardering mag geen moment onder de 700 komen!
|
||||
STR_2392 :{BLACK}Om in één maand ten minste {POP16}{POP16}{CURRENCY} aan attractiekaartjes te verdienen
|
||||
STR_2393 :{BLACK}Om 10 verschillende typen achtbanen in je park te hebben, elk met een lengte van ten minste {LENGTH} en een plezierwaarde van ten minste 7,00
|
||||
STR_2394 :{BLACK}Om alle 5 deels gebouwde achtbanen in je park af te maken, elk met een plezierwaarde van ten minste {POP16}{POP16}{COMMA2DP32}
|
||||
|
@ -2322,7 +2322,7 @@ STR_3305 :{WINDOW_COLOUR_2}Maandelijkse inkomsten:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Maandelijkse winst:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Minimumlengte:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Plezierwaarde:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Attracties met een monumentenstatus:
|
||||
|
@ -3685,6 +3685,11 @@ STR_6614 :Kan de toegangsprijs niet aanpassen
|
|||
STR_6615 :Dit baantype vereist water
|
||||
STR_6616 :Ongeldige actie voor dit type werknemer
|
||||
STR_6617 :Kan een kaartelement niet met zichzelf verwisselen
|
||||
STR_6618 :Kan de beperkingen van dit object niet aanpassen…
|
||||
STR_6619 :Dit objecttype kan niet worden beperkt!
|
||||
STR_6620 :Object niet gevonden!
|
||||
STR_6621 :Beperken
|
||||
STR_6622 :Maakt het object enkel beschikbaar in de scenariobewerker en zandbakmodus.
|
||||
|
||||
#############
|
||||
# Scenarios #
|
||||
|
|
|
@ -1735,12 +1735,12 @@ STR_2382 :Teren
|
|||
STR_2383 :Woda
|
||||
STR_2384 :{WINDOW_COLOUR_2}Twój cel:
|
||||
STR_2385 :{BLACK}Brak
|
||||
STR_2386 :{BLACK}Miej przynajmniej {COMMA16} gości w parku na koniec roku {MONTHYEAR} przy ocenie parku wynoszącej przynajmniej 600
|
||||
STR_2386 :{BLACK}Miej przynajmniej {COMMA32} gości w parku na koniec roku {MONTHYEAR} przy ocenie parku wynoszącej przynajmniej 600
|
||||
STR_2387 :{BLACK}Uzyskaj wartość parku nie mniejszą niż {POP16}{POP16}{CURRENCY} na koniec {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}
|
||||
STR_2388 :{BLACK}Baw się dobrze!
|
||||
STR_2389 :{BLACK}Wybuduj tę atrakcję ({STRINGID}) najlepiej jak potrafisz!
|
||||
STR_2390 :{BLACK}Zbuduj w parku 10 różnych działających kolejek górskich, każda o współczynniku emocji wynoszącym przynajmniej 6.00
|
||||
STR_2391 :{BLACK}Miej przynajmniej {COMMA16} gości w swoim parku. Nie pozwól aby ocena parku spadła poniżej 700 nawet na chwilę!
|
||||
STR_2391 :{BLACK}Miej przynajmniej {COMMA32} gości w swoim parku. Nie pozwól aby ocena parku spadła poniżej 700 nawet na chwilę!
|
||||
STR_2392 :{BLACK}Uzyskaj miesięczny przychód ze sprzedaży biletów na atrakcje w wysokości przynajmniej {POP16}{POP16}{CURRENCY}
|
||||
STR_2393 :{BLACK}Zbuduj w parku 10 różnych działających kolejek górskich, każda o współczynniku emocji wynoszącym przynajmniej 7.00 i nie krótsza niż {LENGTH}
|
||||
STR_2394 :{BLACK}Dokończ budowę 5 częściowo już wybudowanych kolejek górskich, projektując je tak aby współczynnik emocji każdej z nich nie był mniejszy niż {POP16}{POP16}{COMMA2DP32}
|
||||
|
@ -2321,7 +2321,7 @@ STR_3305 :{WINDOW_COLOUR_2}Miesięczny przychód:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Miesięczny zysk:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Minimalna długość:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Wskaźnik emocji:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Przejażdżki/atrakcje według następującej kolejności:
|
||||
|
|
|
@ -1737,12 +1737,12 @@ STR_2382 :Terreno
|
|||
STR_2383 :Água
|
||||
STR_2384 :{WINDOW_COLOUR_2}Seu objetivo:
|
||||
STR_2385 :{BLACK}Nenhum
|
||||
STR_2386 :{BLACK}Ter pelo menos {COMMA16} visitantes em seu parque até o fim de {MONTHYEAR}, com classificação de parque de pelo menos 600
|
||||
STR_2386 :{BLACK}Ter pelo menos {COMMA32} visitantes em seu parque até o fim de {MONTHYEAR}, com classificação de parque de pelo menos 600
|
||||
STR_2387 :{BLACK}Alcançar um valor do parque de pelo menos {POP16}{POP16}{CURRENCY} até o fim de {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}
|
||||
STR_2388 :{BLACK}Divirta-se!
|
||||
STR_2389 :{BLACK}Construir o melhor {STRINGID} que puder!
|
||||
STR_2390 :{BLACK}Ter 10 tipos de montanha-russa diferentes operando em seu parque, cada uma com um valor de emoção de pelo menos 6.00
|
||||
STR_2391 :{BLACK}Ter pelo menos {COMMA16} visitantes em seu parque. Você não pode deixar a classificação de parque abaixo de 700 em momento algum!
|
||||
STR_2391 :{BLACK}Ter pelo menos {COMMA32} visitantes em seu parque. Você não pode deixar a classificação de parque abaixo de 700 em momento algum!
|
||||
STR_2392 :{BLACK}Alcançar uma receita mensal de ingressos de atrações de pelo menos {POP16}{POP16}{CURRENCY}
|
||||
STR_2393 :{BLACK}Ter 10 tipos de montanha-russa diferentes operando em seu parque, cada uma com um comprimento mínimo de {LENGTH}, e um valor de emoção de no mínimo 7.00
|
||||
STR_2394 :{BLACK}Terminar de construir todas as 5 montanhas-russas parcialmente construídas neste parque, projetando-as para alcançar um valor de emoção de pelo menos {POP16}{POP16}{COMMA2DP32} cada
|
||||
|
@ -2323,7 +2323,7 @@ STR_3305 :{WINDOW_COLOUR_2}Receita Mensal:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Lucro Mensal:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Comprimento Mínimo:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Classificação de emoção:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Atrações em ordem de preservação:
|
||||
|
@ -3686,6 +3686,11 @@ STR_6614 :Não é possível mudar o preço de entrada do parque
|
|||
STR_6615 :A pista nesse azulejo precisa de água
|
||||
STR_6616 :Ação inválida para esse tipo de funcionário
|
||||
STR_6617 :Não é possível trocar o elemento do azulejo com ele mesmo
|
||||
STR_6618 :Não é possível restringir ou liberar o objeto…
|
||||
STR_6619 :Tipo de objeto não pode ser restringido!
|
||||
STR_6620 :Objeto não encontrado!
|
||||
STR_6621 :Restringir
|
||||
STR_6622 :Restringir objeto ao Editor de Cenário e ao modo sandbox.
|
||||
|
||||
#############
|
||||
# Scenarios #
|
||||
|
|
|
@ -1737,12 +1737,12 @@ STR_2382 :Земля
|
|||
STR_2383 :Вода
|
||||
STR_2384 :{WINDOW_COLOUR_2}Ваша задача:
|
||||
STR_2385 :{BLACK}Нет
|
||||
STR_2386 :{BLACK}Иметь не менее {COMMA16} гостей в парке к концу {MONTHYEAR}, и рейтинг парка не менее 600
|
||||
STR_2386 :{BLACK}Иметь не менее {COMMA32} гостей в парке к концу {MONTHYEAR}, и рейтинг парка не менее 600
|
||||
STR_2387 :{BLACK}Поднять стоимость парка до {POP16}{POP16}{CURRENCY} к концу {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}
|
||||
STR_2388 :{BLACK}Удачи!
|
||||
STR_2389 :{BLACK}Постройте самый лучший {STRINGID} что сможете!
|
||||
STR_2390 :{BLACK}Завести 10 различных типов американских горок, каждую с рейтингом волнения не менее 6.00
|
||||
STR_2391 :{BLACK}Иметь не менее {COMMA16} гостей в парке. Нельзя допустить падения рейтинга ниже 700!
|
||||
STR_2391 :{BLACK}Иметь не менее {COMMA32} гостей в парке. Нельзя допустить падения рейтинга ниже 700!
|
||||
STR_2392 :{BLACK}Довести ежемесячный доход от продажи билетов на аттракционы до {POP16}{POP16}{CURRENCY}
|
||||
STR_2393 :{BLACK}Завести 10 различных типов Американских горок, каждая не короче {LENGTH}, и рейтингом волнения не менее 7.00
|
||||
STR_2394 :{BLACK}Закончить строительство всех 5 недостроенных горок, разработав тематику и добившись рейтинга волнения не ниже {POP16}{POP16}{COMMA2DP32} на каждой
|
||||
|
@ -2323,7 +2323,7 @@ STR_3305 :{WINDOW_COLOUR_2}Месячный доход:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Месячная прибыль:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Миним. длина:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Рейтинг волнения:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Аттракционы в зарезервированном порядке:
|
||||
|
|
|
@ -1738,12 +1738,12 @@ STR_2382 :Land
|
|||
STR_2383 :Vatten
|
||||
STR_2384 :{WINDOW_COLOUR_2}Ditt uppdrag:
|
||||
STR_2385 :{BLACK}Inget
|
||||
STR_2386 :{BLACK}Att ha minst {COMMA16} besökare i parken tills {MONTHYEAR}, med ett parkomdöme över 600
|
||||
STR_2386 :{BLACK}Att ha minst {COMMA32} besökare i parken tills {MONTHYEAR}, med ett parkomdöme över 600
|
||||
STR_2387 :{BLACK}Att uppnå ett parkvärde över {POP16}{POP16}{CURRENCY} tills {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}
|
||||
STR_2388 :{BLACK}Ha kul!
|
||||
STR_2389 :{BLACK}Bygg den bästa {STRINGID} du kan!
|
||||
STR_2390 :{BLACK}Att ha tio olika sorters berg- och dalbanor i din park, alla med en glädjenivå över 6.00
|
||||
STR_2391 :{BLACK}Att ha minst {COMMA16} besökare i din park. Du får aldrig låta parkomdömet gå under 700!
|
||||
STR_2391 :{BLACK}Att ha minst {COMMA32} besökare i din park. Du får aldrig låta parkomdömet gå under 700!
|
||||
STR_2392 :{BLACK}Att få en månadsinkomst från åktursbiljetter på minst {POP16}{POP16}{CURRENCY}
|
||||
STR_2393 :{BLACK}Att ha tio olika sorters berg- och dalbanor i din park, alla ska vara längre än {LENGTH}, och med en glädjenivå över 7.00
|
||||
STR_2394 :{BLACK}Att bygga klart alla fem delvis byggda berg- och dalbanor i parken, designade så att de alla får en glädjenivå över {POP16}{POP16}{COMMA2DP32}
|
||||
|
@ -2326,7 +2326,7 @@ STR_3305 :{WINDOW_COLOUR_2}Månadsinkomst:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Månadsvinst:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Minimilängd:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Glädjenivå:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Åkturer och attraktioner som måste bevaras:
|
||||
|
|
|
@ -1731,12 +1731,12 @@ STR_2382 :Arazi/Zemin
|
|||
STR_2383 :Su
|
||||
STR_2384 :{WINDOW_COLOUR_2}Hedefiniz:
|
||||
STR_2385 :{BLACK}Yok
|
||||
STR_2386 :{BLACK}Parkınızda en az {COMMA16} müşteri {MONTHYEAR} sonuna kadar bulunması ve park değerlendirmesinin en az 600 olması gerekli
|
||||
STR_2386 :{BLACK}Parkınızda en az {COMMA32} müşteri {MONTHYEAR} sonuna kadar bulunması ve park değerlendirmesinin en az 600 olması gerekli
|
||||
STR_2387 :{BLACK}Park değeri en az {POP16}{POP16}{CURRENCY} {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR} kadar olmalı
|
||||
STR_2388 :{BLACK}Iyi eğlenceler!
|
||||
STR_2389 :{BLACK}Kendinize göre en iyi {STRINGID} inşa ediniz!
|
||||
STR_2390 :{BLACK}Parkınızda heyecan oranı en az 6.00 olan, 10 çesitli hız treni inşa edilmeli
|
||||
STR_2391 :{BLACK}Parkınızda en az {COMMA16} müşteri bulunması ve park değerlendirmesinin hiç bir zaman 700’den aşağı düşmemesi gerekli
|
||||
STR_2391 :{BLACK}Parkınızda en az {COMMA32} müşteri bulunması ve park değerlendirmesinin hiç bir zaman 700’den aşağı düşmemesi gerekli
|
||||
STR_2392 :{BLACK}Alet bileti satışlarından aylık en az {POP16}{POP16}{CURRENCY} geliriniz olmalı
|
||||
STR_2393 :{BLACK}Parkınızda heyecan oranı en az 7.00 ve uzunluğu en az {LENGTH} olan 10 çeşitli hız treni inşa edilmeli
|
||||
STR_2394 :{BLACK}Parkınızda bulunan 5 kısmen inşa edilmiş hız trenlerinin inşasını en az {POP16}{POP16}{COMMA2DP32} heyecan oranı ile tamamlayınız
|
||||
|
@ -2317,7 +2317,7 @@ STR_3305 :{WINDOW_COLOUR_2}Aylık gelir:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Aylık kâr:
|
||||
STR_3307 :{WINDOW_COLOUR_2}En az uzunluk:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Heyecan oranı:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Koruma altına almak istediğiniz aletleri seçiniz
|
||||
|
|
|
@ -1737,12 +1737,12 @@ STR_2382 :Ділянка
|
|||
STR_2383 :Вода
|
||||
STR_2384 :{WINDOW_COLOUR_2}Ваша мета:
|
||||
STR_2385 :{BLACK}Немає
|
||||
STR_2386 :{BLACK}Мати щонайменше {COMMA16} гостей у парку наприкінці {MONTHYEAR}, з рейтингом парку не менше 600
|
||||
STR_2386 :{BLACK}Мати щонайменше {COMMA32} гостей у парку наприкінці {MONTHYEAR}, з рейтингом парку не менше 600
|
||||
STR_2387 :{BLACK}Досягти вартості парку щонайменше {POP16}{POP16}{CURRENCY} наприкінці {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}
|
||||
STR_2388 :{BLACK}Розважатися!
|
||||
STR_2389 :{BLACK}Збудувати найкращий {STRINGID}, який можете!
|
||||
STR_2390 :{BLACK}Мати 10 різних типів американських гірок, що працюють у парку, кожна з яких має коефіцієнт захоплення не менше 6.00
|
||||
STR_2391 :{BLACK}Мати щонайменше {COMMA16} гостей у парку. Ви не повинні допустити, щоб рейтинг парку опустився нижче 700 в будь-який час!
|
||||
STR_2391 :{BLACK}Мати щонайменше {COMMA32} гостей у парку. Ви не повинні допустити, щоб рейтинг парку опустився нижче 700 в будь-який час!
|
||||
STR_2392 :{BLACK}Досягти щомісячного доходу від продажу квитків на атракціон у розмірі щонайменше {POP16}{POP16}{CURRENCY}
|
||||
STR_2393 :{BLACK}To have 10 different types of roller coasters operating in your park, each with a minimum length of {LENGTH}, and an excitement rating of at least 7.00
|
||||
STR_2394 :{BLACK}Завершити будівництво всіх 5 частково побудованих американських гірок у цьому парку, спланувавши їх так, щоб досягти оцінки захоплення не менше {POP16}{POP16}{COMMA2DP32} для кожної з них
|
||||
|
@ -2356,7 +2356,7 @@ STR_3305 :{WINDOW_COLOUR_2}Щомісячний дохід:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Щомісячний прибуток:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Мінімальна довжина:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Оцінка захоплення:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Rides/attractions under a preservation order:
|
||||
|
@ -3695,6 +3695,36 @@ STR_6589 :Показати кнопки вікна зліва
|
|||
STR_6590 :Показати кнопки вікна (напр. для закриття вікна) ліворуч від рядка заголовка.
|
||||
STR_6591 :Не можна звільнити поки працівник лагодить атракціон.
|
||||
STR_6592 :Не можна звільнити поки працівник тестує атракціон.
|
||||
STR_6593 :Прибрати паркани
|
||||
STR_6594 :Інспектор тайлів: Увімк./Вимк. нахили стін
|
||||
STR_6595 :{WINDOW_COLOUR_2}Автор: {BLACK}{STRING}
|
||||
STR_6596 :{WINDOW_COLOUR_2}Автори: {BLACK}{STRING}
|
||||
STR_6597 :Невірний параметр
|
||||
STR_6598 :Значення поза діапазону
|
||||
STR_6599 :Примарний елемент не знайдено
|
||||
STR_6600 :Кулю не знайдено
|
||||
STR_6601 :Персонал не знайдено
|
||||
STR_6602 :Атракціон не знайдено
|
||||
STR_6603 :Ride object entry not found
|
||||
STR_6604 :Гравця не знайдено
|
||||
STR_6605 :Елемент входу не знайдено
|
||||
STR_6606 :Елемент поверхні не знайдено
|
||||
STR_6607 :Елемент тайлу не знайдено
|
||||
STR_6608 :Елемент рейки не знайдено
|
||||
STR_6609 :Блок рейки не знайдено
|
||||
STR_6610 :Елемент стежки не знайдено
|
||||
STR_6611 :Елемент стіни не знайдено
|
||||
STR_6612 :Елемент воріт не знайдено
|
||||
STR_6613 :Перезавантажити об'єкт
|
||||
STR_6614 :Не можна змінити ціну на вхід
|
||||
STR_6615 :Доріжка на цьому тайлі потребує води
|
||||
STR_6616 :Цьому типу персоналу дія недопустима
|
||||
STR_6617 :Неможливо замінити елемент тайлу тим же елементом
|
||||
STR_6618 :Не вдається обмежити/зняти обмеження об'єкту...
|
||||
STR_6619 :Тип об'єкту неможливо обмежити!
|
||||
STR_6620 :Об'єкт не знайдено!
|
||||
STR_6621 :Обмежити
|
||||
STR_6622 :Обмежити використання об'єкта лише в Редакторі Сценаріїв та в режимі пісочниці.
|
||||
|
||||
#############
|
||||
# Scenarios #
|
||||
|
@ -4527,4 +4557,4 @@ STR_DTLS :Привіт, дітки! Нумо розважатися!{NEWLINE}
|
|||
<Sand Dune>
|
||||
STR_SCNR :Піщана дюна
|
||||
STR_PARK :Піщана дюна
|
||||
STR_DTLS :Як власник невеликого парку, ви купили велику ділянку землі вздовж пляжу, щоб розширити його і привабити більше гостей до прекрасних піщаних дюн, але будьте обережні: ви не можете порушувати ці екологічно крихкі піщані дюни.{NEWLINE}Автор: rbarclay
|
||||
STR_DTLS :Як власник невеликого парку, ви купили велику ділянку землі вздовж пляжу, щоб розширити його і привабити більше гостей до прекрасних піщаних дюн, але будьте обережні: ви не можете порушувати ці екологічно крихкі піщані дюни.{NEWLINE}Автор: rbarclay
|
||||
|
|
|
@ -1734,12 +1734,12 @@ STR_2382 :Land
|
|||
STR_2383 :Water
|
||||
STR_2384 :{WINDOW_COLOUR_2}Mục tiêu:
|
||||
STR_2385 :{BLACK}Không
|
||||
STR_2386 :{BLACK}Có ít nhất {COMMA16} khách vào cuối tháng {MONTHYEAR}, với đánh giá công viên từ 600 trở lên
|
||||
STR_2386 :{BLACK}Có ít nhất {COMMA32} khách vào cuối tháng {MONTHYEAR}, với đánh giá công viên từ 600 trở lên
|
||||
STR_2387 :{BLACK}Đạt giá trị công viên ít nhất {POP16}{POP16}{CURRENCY} vào cuối {PUSH16}{PUSH16}{PUSH16}{PUSH16}{PUSH16}{MONTHYEAR}
|
||||
STR_2388 :{BLACK}Have Fun!
|
||||
STR_2389 :{BLACK}Build the best {STRINGID} you can!
|
||||
STR_2390 :{BLACK}Có 10 loại tàu lượn khác nhau trong công viên, mỗi cái có chỉ số phấn khích ít nhất 6.00
|
||||
STR_2391 :{BLACK}Có ít nhất {COMMA16} khách. Không được để đánh giá công viên thấp hơn 700 ở bất kỳ thời điểm nào!
|
||||
STR_2391 :{BLACK}Có ít nhất {COMMA32} khách. Không được để đánh giá công viên thấp hơn 700 ở bất kỳ thời điểm nào!
|
||||
STR_2392 :{BLACK}To achieve a monthly income from ride tickets of at least {POP16}{POP16}{CURRENCY}
|
||||
STR_2393 :{BLACK}To have 10 different types of roller coasters operating in your park, each with a minimum length of {LENGTH}, and an excitement rating of at least 7.00
|
||||
STR_2394 :{BLACK}To finish building all 5 of the partially built roller coasters in this park, designing them to achieve excitement ratings of at least {POP16}{POP16}{COMMA2DP32} each
|
||||
|
@ -2321,7 +2321,7 @@ STR_3305 :{WINDOW_COLOUR_2}Monthly income:
|
|||
STR_3306 :{WINDOW_COLOUR_2}Monthly profit:
|
||||
STR_3307 :{WINDOW_COLOUR_2}Minimum length:
|
||||
STR_3308 :{WINDOW_COLOUR_2}Độ phấn khích:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}Rides/attractions under a preservation order:
|
||||
|
|
|
@ -1737,12 +1737,12 @@ STR_2382 :土地
|
|||
STR_2383 :水面
|
||||
STR_2384 :{WINDOW_COLOUR_2}你的目标:
|
||||
STR_2385 :{BLACK}无
|
||||
STR_2386 :{BLACK}在{POP16}{MONTHYEAR}末乐园内至少有{NEWLINE}{PUSH16}{PUSH16}{COMMA16}游客,并且乐园评分不低于600
|
||||
STR_2386 :{BLACK}在{POP16}{MONTHYEAR}末乐园内至少有{NEWLINE}{PUSH16}{PUSH16}{COMMA32}游客,并且乐园评分不低于600
|
||||
STR_2387 :{BLACK}在{POP16}{MONTHYEAR}末乐园价值不低于{CURRENCY}
|
||||
STR_2388 :{BLACK}尽情玩吧!
|
||||
STR_2389 :{BLACK}建造你的最佳{STRINGID}吧!
|
||||
STR_2390 :{BLACK}在你的乐园中运营10种类型的过山车,并且每一个的兴奋度不低于6.00
|
||||
STR_2391 :{BLACK}乐园内至少有{COMMA16}游客,并且在任何时候你都不能让公园评分低于700!
|
||||
STR_2391 :{BLACK}乐园内至少有{COMMA32}游客,并且在任何时候你都不能让公园评分低于700!
|
||||
STR_2392 :{BLACK}每个月的门票收入至少{NEWLINE}{POP16}{POP16}{CURRENCY}
|
||||
STR_2393 :{BLACK}在你的乐园中运营10种类型的过山车,每一个的长度至少{LENGTH},并且每一个的兴奋度不低于7.00
|
||||
STR_2394 :{BLACK}建造好五座未完成的过山车,将他们每座设计成兴奋度至少达到{POP16}{POP16}{COMMA2DP32}
|
||||
|
@ -2323,7 +2323,7 @@ STR_3305 :{WINDOW_COLOUR_2}每月收入:
|
|||
STR_3306 :{WINDOW_COLOUR_2}每月利润:
|
||||
STR_3307 :{WINDOW_COLOUR_2}最少长度:
|
||||
STR_3308 :{WINDOW_COLOUR_2}兴奋度:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}请选取要保留的游乐设施/店铺:
|
||||
|
|
|
@ -1741,12 +1741,12 @@ STR_2382 :土地
|
|||
STR_2383 :水塘
|
||||
STR_2384 :{WINDOW_COLOUR_2}你的目標:
|
||||
STR_2385 :{BLACK}無
|
||||
STR_2386 :{BLACK}在{POP16}{MONTHYEAR}尾之前, 樂園裡至少有{PUSH16}{PUSH16}{COMMA16}個遊客, 而且樂園評價並不低於600
|
||||
STR_2386 :{BLACK}在{POP16}{MONTHYEAR}尾之前, 樂園裡至少有{PUSH16}{PUSH16}{COMMA32}個遊客, 而且樂園評價並不低於600
|
||||
STR_2387 :{BLACK}在{POP16}{MONTHYEAR}尾之前, 樂園價值必需達到{CURRENCY}
|
||||
STR_2388 :{BLACK}盡情玩吧!
|
||||
STR_2389 :{BLACK}建造你的最佳{STRINGID}吧!
|
||||
STR_2390 :{BLACK}樂園必需建造十種不同種類的雲霄飛車, 而且每座的興奮度不低於6.00
|
||||
STR_2391 :{BLACK}樂園裡至少有{COMMA16}個遊客. 任何時間下, 你並不可以令樂園評價跌至700以下!
|
||||
STR_2391 :{BLACK}樂園裡至少有{COMMA32}個遊客. 任何時間下, 你並不可以令樂園評價跌至700以下!
|
||||
STR_2392 :{BLACK}遊樂設施的門票收入至少要達到每月{POP16}{POP16}{CURRENCY}
|
||||
STR_2393 :{BLACK}樂園必需建造十種不同種類的雲霄飛車, 每座的長度至少需要{LENGTH}, 而且興奮度都不低於7.00
|
||||
STR_2394 :{BLACK}建造好五座未完成的雲霄飛車, 將他們每座設計成興奮度至少達到{POP16}{POP16}{COMMA2DP32}
|
||||
|
@ -2327,7 +2327,7 @@ STR_3305 :{WINDOW_COLOUR_2}每月收入:
|
|||
STR_3306 :{WINDOW_COLOUR_2}每月利潤:
|
||||
STR_3307 :{WINDOW_COLOUR_2}最少長度:
|
||||
STR_3308 :{WINDOW_COLOUR_2}興奮度:
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA16}
|
||||
STR_3309 :{WINDOW_COLOUR_2}{COMMA32}
|
||||
STR_3310 :{WINDOW_COLOUR_2}{LENGTH}
|
||||
STR_3311 :{WINDOW_COLOUR_2}{COMMA2DP32}
|
||||
STR_3312 :{WINDOW_COLOUR_2}請選取要保留的遊樂設施/店鋪:
|
||||
|
|
|
@ -1,3 +1,44 @@
|
|||
0.4.10 (in development)
|
||||
------------------------------------------------------------------------
|
||||
- Feature: [#18171] Add port of the RCT1 Stand-Up Roller Coaster.
|
||||
- Feature: [#21590] [Plugin] Plugins can now read and write banner properties of tile elements.
|
||||
- Feature: [#21636] Add shortcut key for sorting tile elements.
|
||||
- Feature: [objects#294] Add scenery versions of wooden truss supports.
|
||||
- Feature: [objects#295] Flipped version of wooden post.
|
||||
- Improved: [#21424] Extra viewports can now rotate independently from the main viewport.
|
||||
- Improved: [#21561, #21631] Enable more features in Android build (plugins, networking, multiplayer, audio formats).
|
||||
- Improved: [#21599] Currency signs now use non-breaking spaces.
|
||||
- Improved: [objects#157] Added sloped images for many walls.
|
||||
- Improved: [objects#288] Better map colours and more sensible prices for RCT1 land surfaces.
|
||||
- Improved: [objects#292] Vehicle colour cleanups for WW/TT vehicles.
|
||||
- Improved: [objects#299] More accurate ratings modifiers for RCT1 vehicles.
|
||||
- Improved: [objects#309] Updated names for dodgems and flying saucers vehicles.
|
||||
- Improved: [objects#313] buildMenuPriority for dodgems and flying saucers vehicles.
|
||||
- Change: [#21529] Classify “Southern Sands”, “Tiny Towers”, “Nevermore Park”, “Pacifica” as expert scenarios.
|
||||
- Change: [#21545] Reorder Wacky Worlds scenarios and adjust their difficulty classification.
|
||||
- Fix: [#910] Extra viewport does not preserve the location when rotating.
|
||||
- Fix: [#18413] Crash when mouse over a hacked train.
|
||||
- Fix: [#20338] Cannot select Scenery Picker or Scatter Tool when the scenery recolouring tool is active.
|
||||
- Fix: [#21317] Track designer allows proceeding without an object selected.
|
||||
- Fix: [#21360] If the object selection is missing certain types, the Object Selection window will switch to an incorrect tab.
|
||||
- Fix: [#21419] Cannot place walls underground beneath sloped tiles with clearance checks disabled.
|
||||
- Fix: [#21434] Number of guests overflows in objective text.
|
||||
- Fix: [#21522] Supports for 3×3 turns and 45 degree turns on the Hybrid Coaster and Wooden Roller Coaster not drawn correctly.
|
||||
- Fix: [#21543] Crash with creating a TrackIterator with invalid arguments.
|
||||
- Fix: [#21635] Tile inspector hotkey can set wall slope for non-slopeable objects.
|
||||
- Fix: [#21641] Crash when creating track iterator from an invalid tile element.
|
||||
- Fix: [#21652] Dialog window to confirm overwriting files does not apply the theme colours correctly.
|
||||
- Fix: [#21654] No sound effects when using RCT Classic as an asset base.
|
||||
- Fix: [#21654] Extraneous reports of an object conflict between `rct2.audio.base` and `rct2.audio.base.rctc`.
|
||||
- Fix: [#21664] Crash when switching between languages that use TTF.
|
||||
- Fix: [#21668] Crash when on null ride in Guest::UpdateRideLeaveExit.
|
||||
- Fix: [#21691] Crash when validating rides which can't contain banked track.
|
||||
- Fix: [objects#290] “Haunted Mansion” cars have a non-functional third remap colour.
|
||||
- Fix: [objects#296] Incorrect wall placement around large Kremlin/drab pieces.
|
||||
- Fix: [objects#300] Incorrect Colosseum and volcano corner clearances.
|
||||
- Fix: [objects#319] Incorrect diagonal slope images used for RCT1 corkscrew.
|
||||
- Fix: [objects#320] Incorrect Mandarin Duck boats capacity.
|
||||
|
||||
0.4.9 (2024-03-02)
|
||||
------------------------------------------------------------------------
|
||||
- Feature: [#20376] Add Ukrainian language.
|
||||
|
@ -24,6 +65,7 @@
|
|||
- Fix: [#21220] When creating a new park from a SC4 file, the localised park name is not applied.
|
||||
- Fix: [#21286] Cannot build unbanking turns with RCT1 vehicles.
|
||||
- Fix: [#21288] Text overlaps in the “About ‘OpenRCT2’” window for Arabic, Chinese, Japanese, Korean and Vietnamese.
|
||||
- Fix: [#21289] Text overlaps and overflows in the map window for some languages.
|
||||
- Fix: [#21310] Some half loop elements require more clearance than their upward/downward counterparts.
|
||||
- Fix: [#21318] Virtual Floor for building scenery is not properly invalidated.
|
||||
- Fix: [#21330] Tooltips from dropdown widgets have the wrong position.
|
||||
|
@ -57,7 +99,7 @@
|
|||
- Fix: [#21039] Text rendering bleeds pixels through windows.
|
||||
- Fix: [#21054] “No entrance” style is selected by default in the track designer.
|
||||
- Fix: [#21145] [Plugin] setInterval/setTimeout handle conflict.
|
||||
- Fix: [#21157] [Plugin] Widgets do not redraw correctly when updating disabled or visibility state.
|
||||
- Fix: [#21157] [Plugin] Widgets do not redraw correctly when updating disabled or visibility state.
|
||||
- Fix: [#21158] [Plugin] Potential crash using setInterval/setTimeout within the callback.
|
||||
- Fix: [#21171] [Plugin] Crash creating entities with no more entity slots available.
|
||||
- Fix: [#21178] Inca Lost City’s scenario description incorrectly states there are height restrictions.
|
||||
|
|
|
@ -1503,12 +1503,12 @@ declare global {
|
|||
|
||||
edges: number;
|
||||
corners: number;
|
||||
slopeDirection: number | null;
|
||||
slopeDirection: Direction | null;
|
||||
isBlockedByVehicle: boolean;
|
||||
isWide: boolean;
|
||||
|
||||
isQueue: boolean;
|
||||
queueBannerDirection: number | null;
|
||||
queueBannerDirection: Direction | null;
|
||||
ride: number | null;
|
||||
station: number | null;
|
||||
|
||||
|
@ -1558,10 +1558,14 @@ declare global {
|
|||
direction: Direction;
|
||||
object: number;
|
||||
primaryColour: number;
|
||||
/** If the element is a banner, this is the text colour. */
|
||||
secondaryColour: number;
|
||||
tertiaryColour: number;
|
||||
bannerIndex: number | null;
|
||||
slope: Direction;
|
||||
/** Writing to bannerIndex is deprecated and may result in uncontrolled behaviour. */
|
||||
readonly bannerIndex: number | null;
|
||||
/** If the element is a banner, this is its text. */
|
||||
bannerText: string | null;
|
||||
}
|
||||
|
||||
interface EntranceElement extends BaseTileElement {
|
||||
|
@ -1582,16 +1586,28 @@ declare global {
|
|||
direction: Direction;
|
||||
object: number;
|
||||
primaryColour: number;
|
||||
/** If the element is a banner, this is the text colour. */
|
||||
secondaryColour: number;
|
||||
tertiaryColour: number;
|
||||
bannerIndex: number | null;
|
||||
sequence: number;
|
||||
/** Writing to bannerIndex is deprecated and may result in uncontrolled behaviour. */
|
||||
readonly bannerIndex: number | null;
|
||||
/** If the element is a banner, this is its text. */
|
||||
bannerText: string | null;
|
||||
}
|
||||
|
||||
interface BannerElement extends BaseTileElement {
|
||||
type: "banner";
|
||||
|
||||
direction: Direction;
|
||||
bannerIndex: number;
|
||||
object: number;
|
||||
primaryColour: number;
|
||||
/** This is the text colour. */
|
||||
secondaryColour: number;
|
||||
/** Writing to bannerIndex is deprecated and may result in uncontrolled behaviour. */
|
||||
readonly bannerIndex: number;
|
||||
bannerText: string;
|
||||
isNoEntry: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3612,8 +3628,9 @@ declare global {
|
|||
|
||||
/**
|
||||
* The amount of funding currently spent on research.
|
||||
* 0: none, 1: minimum, 2: normal, 3: maximum
|
||||
*/
|
||||
funding: ResearchFundingLevel;
|
||||
funding: number;
|
||||
|
||||
/**
|
||||
* The categories of research which should be prioritised.
|
||||
|
@ -3663,7 +3680,7 @@ declare global {
|
|||
* E.g. gentle rides, thrill rides, shops etc.
|
||||
* Note: Any updates to this field are ignored by OpenRCT2, the category will be derived from the ride type.
|
||||
*/
|
||||
readonly category?: ResearchCategory;
|
||||
readonly category: RideResearchCategory;
|
||||
|
||||
/**
|
||||
* The ride type. Each vehicle can have a seperate invention for each ride type.
|
||||
|
@ -3677,7 +3694,7 @@ declare global {
|
|||
}
|
||||
|
||||
interface SceneryResearchItem {
|
||||
readonly category?: "scenery_group";
|
||||
readonly category: "scenery";
|
||||
readonly type: "scenery";
|
||||
|
||||
/**
|
||||
|
@ -3686,23 +3703,15 @@ declare global {
|
|||
readonly object: number;
|
||||
}
|
||||
|
||||
type ResearchItemType = "scenery" | "ride";
|
||||
|
||||
type ResearchCategory =
|
||||
type RideResearchCategory =
|
||||
"transport" |
|
||||
"gentle" |
|
||||
"rollercoaster" |
|
||||
"thrill" |
|
||||
"water" |
|
||||
"shop" |
|
||||
"scenery";
|
||||
"shop";
|
||||
|
||||
enum ResearchFundingLevel {
|
||||
None,
|
||||
Minimum,
|
||||
Normal,
|
||||
Maximum
|
||||
}
|
||||
type ResearchCategory = RideResearchCategory | "scenery";
|
||||
|
||||
type ResearchFundingStage =
|
||||
"initial_research" |
|
||||
|
|
|
@ -45,8 +45,8 @@
|
|||
<LibsSha1 Condition="'$(Platform)'=='ARM64'">bd338aa3da9a357fb996dcaa6cea02c4f906718c</LibsSha1>
|
||||
<TitleSequencesUrl>https://github.com/OpenRCT2/title-sequences/releases/download/v0.4.6/title-sequences.zip</TitleSequencesUrl>
|
||||
<TitleSequencesSha1>80fefc6ebbabc42a6f4703412daa5c62f661420d</TitleSequencesSha1>
|
||||
<ObjectsUrl>https://github.com/OpenRCT2/objects/releases/download/v1.4.0/objects.zip</ObjectsUrl>
|
||||
<ObjectsSha1>f512e230ffa2f16109209e4f30e7d7bf4fe21fc0</ObjectsSha1>
|
||||
<ObjectsUrl>https://github.com/OpenRCT2/objects/releases/download/v1.4.3/objects.zip</ObjectsUrl>
|
||||
<ObjectsSha1>ac78210ef46465c0f51bbffd6fe21845092af48e</ObjectsSha1>
|
||||
<OpenSFXUrl>https://github.com/OpenRCT2/OpenSoundEffects/releases/download/v1.0.5/opensound.zip</OpenSFXUrl>
|
||||
<OpenSFXSha1>b1b1f1b241d2cbff63a1889c4dc5a09bdf769bfb</OpenSFXSha1>
|
||||
<OpenMSXUrl>https://github.com/OpenRCT2/OpenMusic/releases/download/v1.5/openmusic.zip</OpenMSXUrl>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,13 +1,13 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
compileSdk 34
|
||||
buildToolsVersion "34.0.0"
|
||||
ndkVersion "26.2.11394342" // Latest r26c (LTS), to be synced with CI container image
|
||||
namespace "io.openrct2"
|
||||
defaultConfig {
|
||||
applicationId 'io.openrct2'
|
||||
minSdkVersion 19
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 28
|
||||
|
||||
versionCode 2
|
||||
|
@ -17,7 +17,7 @@ android {
|
|||
arguments '-DANDROID_STL=c++_shared'
|
||||
targets 'openrct2', 'openrct2-ui', 'openrct2-cli'
|
||||
// abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
||||
abiFilters 'arm64-v8a'
|
||||
abiFilters 'arm64-v8a', 'x86_64'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,8 +45,8 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'commons-io:commons-io:2.6'
|
||||
implementation 'androidx.appcompat:appcompat:1.4.0'
|
||||
implementation 'commons-io:commons-io:2.13.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ endif()
|
|||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(libs
|
||||
URL https://github.com/OpenRCT2/openrct2-dependencies-android/releases/download/v10/${ANDROID_ABI}-android-dynamic.tar.zst
|
||||
URL https://github.com/OpenRCT2/openrct2-dependencies-android/releases/download/v11/${ANDROID_ABI}-android-dynamic.tar.zst
|
||||
|
||||
SOURCE_DIR "${CMAKE_BINARY_DIR}/libs"
|
||||
|
||||
|
@ -29,6 +29,14 @@ ExternalProject_Add(libs
|
|||
INSTALL_COMMAND ""
|
||||
|
||||
BUILD_BYPRODUCTS
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}crypto${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}vorbis${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}vorbisfile${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}FLAC${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}brotlidec${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}brotlicommon${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}ogg${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}bz2${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}freetype${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}png16${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}SDL2${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
|
@ -53,6 +61,14 @@ ExternalProject_Add(libs
|
|||
add_custom_command(TARGET libs POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
# COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/*.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libcrypto.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libvorbis.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libvorbisfile.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libFLAC.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libbrotlidec.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libbrotlicommon.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libogg.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libbz2.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libicudata.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libicuuc.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/libs/lib/libicui18n.so" ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
|
||||
|
@ -124,13 +140,64 @@ set_target_properties(ssl PROPERTIES IMPORTED_LOCATION
|
|||
)
|
||||
add_dependencies(ssl libs)
|
||||
|
||||
add_library(crypto SHARED IMPORTED)
|
||||
set_target_properties(crypto PROPERTIES IMPORTED_LOCATION
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}crypto${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
)
|
||||
add_dependencies(crypto libs)
|
||||
|
||||
add_library(vorbis SHARED IMPORTED)
|
||||
set_target_properties(vorbis PROPERTIES IMPORTED_LOCATION
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}vorbis${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
)
|
||||
add_dependencies(vorbis libs)
|
||||
|
||||
add_library(vorbisfile SHARED IMPORTED)
|
||||
set_target_properties(vorbisfile PROPERTIES IMPORTED_LOCATION
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}vorbisfile${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
)
|
||||
add_dependencies(vorbisfile libs)
|
||||
|
||||
add_library(FLAC SHARED IMPORTED)
|
||||
set_target_properties(FLAC PROPERTIES IMPORTED_LOCATION
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}FLAC${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
)
|
||||
add_dependencies(FLAC libs)
|
||||
|
||||
add_library(brotlidec SHARED IMPORTED)
|
||||
set_target_properties(brotlidec PROPERTIES IMPORTED_LOCATION
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}brotlidec${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
)
|
||||
add_dependencies(brotlidec libs)
|
||||
|
||||
add_library(brotlicommon SHARED IMPORTED)
|
||||
set_target_properties(brotlicommon PROPERTIES IMPORTED_LOCATION
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}brotlicommon${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
)
|
||||
add_dependencies(brotlicommon libs)
|
||||
|
||||
add_library(ogg SHARED IMPORTED)
|
||||
set_target_properties(ogg PROPERTIES IMPORTED_LOCATION
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}ogg${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
)
|
||||
add_dependencies(ogg libs)
|
||||
|
||||
add_library(bz2 SHARED IMPORTED)
|
||||
set_target_properties(bz2 PROPERTIES IMPORTED_LOCATION
|
||||
${CMAKE_BINARY_DIR}/libs/lib/${CMAKE_SHARED_LIBRARY_PREFIX}bz2${CMAKE_SHARED_LIBRARY_SUFFIX}
|
||||
)
|
||||
add_dependencies(bz2 libs)
|
||||
|
||||
|
||||
include_directories(SYSTEM "${CMAKE_BINARY_DIR}/libs/include")
|
||||
include_directories(SYSTEM "${CMAKE_BINARY_DIR}/libs/include/freetype2")
|
||||
include_directories(SYSTEM "${CMAKE_BINARY_DIR}/libs/include/SDL2")
|
||||
|
||||
# now build app's shared lib
|
||||
add_definitions(-DDISABLE_HTTP -DDISABLE_DISCORD_RPC -DDISABLE_HTTP -DDISABLE_NETWORK -DDISABLE_FLAC -DDISABLE_VORBIS -DDISABLE_OPENGL -DGL_GLEXT_PROTOTYPES -D__STDC_LIMIT_MACROS -DNO_TTF -DSDL_MAIN_HANDLED)
|
||||
add_definitions(-DDISABLE_DISCORD_RPC -DDISABLE_OPENGL -DGL_GLEXT_PROTOTYPES -D__STDC_LIMIT_MACROS -DNO_TTF -DSDL_MAIN_HANDLED)
|
||||
|
||||
# Enable scripting
|
||||
add_definitions(-DENABLE_SCRIPTING)
|
||||
|
||||
# Fix SpeexDSP compilation
|
||||
add_definitions(-DHAVE_STDINT_H)
|
||||
|
@ -142,9 +209,12 @@ set(COMMON_COMPILE_OPTIONS "${COMMON_COMPILE_OPTIONS} -fPIC")
|
|||
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--undefined=Java_org_libsdl_app_SDLActivity_nativeInit")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++1z ${COMMON_COMPILE_OPTIONS} -Wnon-virtual-dtor")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++20 ${COMMON_COMPILE_OPTIONS} -Wnon-virtual-dtor")
|
||||
get_filename_component(ORCT2_ROOT "${CMAKE_SOURCE_DIR}/../../../../../" REALPATH)
|
||||
|
||||
# Enable char8_t<->char conversion
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-char8_t -Wno-deprecated-declarations")
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${ORCT2_ROOT}/cmake")
|
||||
|
||||
find_package(CCache)
|
||||
|
@ -159,6 +229,7 @@ if (CCache_FOUND)
|
|||
endif (CCache_FOUND)
|
||||
|
||||
file(GLOB_RECURSE LIBOPENRCT2_SOURCES
|
||||
"${ORCT2_ROOT}/src/thirdparty/duktape/duktape.cpp"
|
||||
"${ORCT2_ROOT}/src/openrct2/*.cpp"
|
||||
"${ORCT2_ROOT}/src/openrct2/*.h"
|
||||
"${ORCT2_ROOT}/src/openrct2/*.hpp")
|
||||
|
@ -179,20 +250,20 @@ file(GLOB_RECURSE OPENRCT2_CLI_SOURCES
|
|||
"${ORCT2_ROOT}/src/openrct2-cli/*.hpp")
|
||||
|
||||
add_library(openrct2 SHARED ${LIBOPENRCT2_SOURCES})
|
||||
target_link_libraries(openrct2 android stdc++ log dl SDL2 png z icu icuuc icudata)
|
||||
target_link_libraries(openrct2 android stdc++ log dl SDL2 png z icu icuuc icudata crypto ssl)
|
||||
|
||||
add_library(openrct2-ui SHARED ${OPENRCT2_GUI_SOURCES})
|
||||
target_link_libraries(openrct2-ui openrct2 android stdc++ GLESv1_CM GLESv2 SDL2main speexdsp)
|
||||
target_link_libraries(openrct2-ui openrct2 android stdc++ GLESv1_CM GLESv2 SDL2main speexdsp brotlicommon brotlidec bz2 freetype ogg vorbis vorbisfile FLAC)
|
||||
|
||||
add_executable(openrct2-cli ${OPENRCT2_CLI_SOURCES})
|
||||
target_link_libraries(openrct2-cli openrct2 android stdc++ GLESv1_CM GLESv2)
|
||||
|
||||
target_include_directories(openrct2 PRIVATE "${ORCT2_ROOT}/src/thirdparty/duktape/")
|
||||
target_include_directories(openrct2 SYSTEM PRIVATE "${ORCT2_ROOT}/src/thirdparty")
|
||||
target_include_directories(openrct2-ui PRIVATE "${ORCT2_ROOT}/src/thirdparty/duktape/")
|
||||
target_include_directories(openrct2-ui PRIVATE "${ORCT2_ROOT}/src")
|
||||
target_include_directories(openrct2-ui SYSTEM PRIVATE "${ORCT2_ROOT}/src/thirdparty")
|
||||
target_include_directories(openrct2-cli PRIVATE "${ORCT2_ROOT}/src")
|
||||
|
||||
# Header-only nlohmann library is installed in CI images at /nlohmann.
|
||||
# To be tiny bit more specific, use '/nlohmann/../' instead of just '/'
|
||||
target_include_directories(openrct2 PRIVATE "/nlohmann/../")
|
||||
target_include_directories(openrct2-ui PRIVATE "/nlohmann/../")
|
||||
target_include_directories(openrct2 PRIVATE "/opt/openrct2/include/nlohmann/../")
|
||||
target_include_directories(openrct2-ui PRIVATE "/opt/openrct2/include/nlohmann/../")
|
||||
|
|
|
@ -80,6 +80,8 @@ public class GameActivity extends SDLActivity {
|
|||
return new String[]{
|
||||
"c++_shared",
|
||||
"speexdsp",
|
||||
"bz2",
|
||||
"freetype",
|
||||
"z",
|
||||
"png16",
|
||||
"SDL2",
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
package io.openrct2;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public class HttpAndroid {
|
||||
private static final String TAG = "HttpAndroid";
|
||||
|
||||
// Corresponding Java enum for the C++ 'Status' enum
|
||||
public enum Status {
|
||||
Invalid(0),
|
||||
Ok(200),
|
||||
NotFound(404);
|
||||
|
||||
private final int code;
|
||||
Status(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
// Corresponding Java enum for the C++ 'Method' enum
|
||||
public enum Method {
|
||||
GET,
|
||||
POST,
|
||||
PUT
|
||||
}
|
||||
|
||||
// Java class equivalent to the C++ 'Response' struct
|
||||
public static class Response {
|
||||
Status status;
|
||||
String contentType;
|
||||
String body;
|
||||
Map<String, List<String>> headers;
|
||||
String error;
|
||||
|
||||
}
|
||||
|
||||
// Java class equivalent to the C++ 'Request' struct
|
||||
public static class Request {
|
||||
String url;
|
||||
Map<String, String> headers;
|
||||
Method method;
|
||||
String body;
|
||||
boolean forceIPv4;
|
||||
|
||||
public Request() {
|
||||
this.method = Method.GET; // Default method
|
||||
}
|
||||
}
|
||||
|
||||
public static Response request(Request request) {
|
||||
Response response = new Response();
|
||||
response.status = Status.Invalid;
|
||||
response.error = "Request failed";
|
||||
try {
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
URL url = new URL(request.url);
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setRequestMethod(request.method.toString());
|
||||
connection.setConnectTimeout(10000);
|
||||
connection.setReadTimeout(10000);
|
||||
connection.setUseCaches(false);
|
||||
connection.setDoInput(true);
|
||||
connection.setInstanceFollowRedirects(true);
|
||||
if (request.headers != null) {
|
||||
for (Map.Entry<String, String> entry : request.headers.entrySet()) {
|
||||
connection.setRequestProperty(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
connection.connect();
|
||||
if (request.body!= null) {
|
||||
OutputStream os = connection.getOutputStream();
|
||||
os.write(request.body.getBytes());
|
||||
os.flush();
|
||||
os.close();
|
||||
}
|
||||
int responseCode = connection.getResponseCode();
|
||||
if (responseCode == 200) {
|
||||
inputStream = connection.getInputStream();
|
||||
} else {
|
||||
inputStream = connection.getErrorStream();
|
||||
}
|
||||
// Log
|
||||
Log.d(TAG, "Request: " + request.url + ", response code: " + responseCode);
|
||||
response.status = Status.Ok;
|
||||
response.contentType = connection.getContentType();
|
||||
response.headers = connection.getHeaderFields();
|
||||
response.body = IOUtils.toString(inputStream, "UTF-8");
|
||||
response.error = null;
|
||||
return response;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error while requesting " + request.url + ", error: " + e.getMessage(), e);
|
||||
response.error = e.getMessage();
|
||||
return response;
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error while requesting " + request.url, e);
|
||||
response.error = e.getMessage();
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ buildscript {
|
|||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.3.0-alpha10'
|
||||
classpath 'com.android.tools.build:gradle:8.3.0'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
android.enableJetifier=true
|
||||
android.enableJetifier=false
|
||||
android.useAndroidX=true
|
||||
org.gradle.configuration-cache=true
|
||||
org.gradle.jvmargs=-Xmx4096m
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include <SDL.h>
|
||||
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
SDLException::SDLException(const std::string& message)
|
||||
: runtime_error(message.c_str())
|
||||
{
|
||||
|
|
|
@ -12,19 +12,22 @@
|
|||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* An exception which wraps an SDL error.
|
||||
*/
|
||||
class SDLException : public std::runtime_error
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
public:
|
||||
explicit SDLException(const std::string& message);
|
||||
|
||||
explicit SDLException(const char* message);
|
||||
|
||||
/**
|
||||
* Throws an SDL exception with a message containing the given call information
|
||||
* and the message given by SDL_GetError.
|
||||
* An exception which wraps an SDL error.
|
||||
*/
|
||||
static void Throw(const char* call);
|
||||
};
|
||||
class SDLException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit SDLException(const std::string& message);
|
||||
|
||||
explicit SDLException(const char* message);
|
||||
|
||||
/**
|
||||
* Throws an SDL exception with a message containing the given call information
|
||||
* and the message given by SDL_GetError.
|
||||
*/
|
||||
static void Throw(const char* call);
|
||||
};
|
||||
} // namespace OpenRCT2::Ui
|
||||
|
|
|
@ -177,7 +177,7 @@ public:
|
|||
SDL_WINDOW_FULLSCREEN,
|
||||
SDL_WINDOW_FULLSCREEN_DESKTOP,
|
||||
};
|
||||
uint32_t windowFlags = _sdlFullscreenFlags[static_cast<int32_t>(mode)];
|
||||
uint32_t windowFlags = _sdlFullscreenFlags[EnumValue(mode)];
|
||||
|
||||
// HACK Changing window size when in fullscreen usually has no effect
|
||||
if (mode == FULLSCREEN_MODE::FULLSCREEN)
|
||||
|
|
|
@ -20,35 +20,35 @@ namespace OpenRCT2
|
|||
{
|
||||
struct IContext;
|
||||
struct IPlatformEnvironment;
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
struct FileDialogDesc;
|
||||
class InGameConsole;
|
||||
struct IUiContext;
|
||||
|
||||
struct IPlatformUiContext
|
||||
{
|
||||
virtual ~IPlatformUiContext() = default;
|
||||
virtual void SetWindowIcon(SDL_Window* window) abstract;
|
||||
virtual bool IsSteamOverlayAttached() abstract;
|
||||
|
||||
virtual void ShowMessageBox(SDL_Window* window, const std::string& message) abstract;
|
||||
virtual bool HasMenuSupport() abstract;
|
||||
virtual int32_t ShowMenuDialog(
|
||||
const std::vector<std::string>& options, const std::string& title, const std::string& text) abstract;
|
||||
virtual void OpenFolder(const std::string& path) abstract;
|
||||
|
||||
virtual void OpenURL(const std::string& url) abstract;
|
||||
virtual std::string ShowFileDialog(SDL_Window* window, const FileDialogDesc& desc) abstract;
|
||||
virtual std::string ShowDirectoryDialog(SDL_Window* window, const std::string& title) abstract;
|
||||
|
||||
virtual bool HasFilePicker() const abstract;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::unique_ptr<IUiContext> CreateUiContext(const std::shared_ptr<IPlatformEnvironment>& env);
|
||||
[[nodiscard]] std::unique_ptr<IPlatformUiContext> CreatePlatformUiContext();
|
||||
|
||||
[[nodiscard]] InGameConsole& GetInGameConsole();
|
||||
} // namespace Ui
|
||||
} // namespace OpenRCT2
|
||||
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
struct FileDialogDesc;
|
||||
class InGameConsole;
|
||||
struct IUiContext;
|
||||
|
||||
struct IPlatformUiContext
|
||||
{
|
||||
virtual ~IPlatformUiContext() = default;
|
||||
virtual void SetWindowIcon(SDL_Window* window) abstract;
|
||||
virtual bool IsSteamOverlayAttached() abstract;
|
||||
|
||||
virtual void ShowMessageBox(SDL_Window* window, const std::string& message) abstract;
|
||||
virtual bool HasMenuSupport() abstract;
|
||||
virtual int32_t ShowMenuDialog(
|
||||
const std::vector<std::string>& options, const std::string& title, const std::string& text) abstract;
|
||||
virtual void OpenFolder(const std::string& path) abstract;
|
||||
|
||||
virtual void OpenURL(const std::string& url) abstract;
|
||||
virtual std::string ShowFileDialog(SDL_Window* window, const FileDialogDesc& desc) abstract;
|
||||
virtual std::string ShowDirectoryDialog(SDL_Window* window, const std::string& title) abstract;
|
||||
|
||||
virtual bool HasFilePicker() const abstract;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::unique_ptr<IUiContext> CreateUiContext(const std::shared_ptr<IPlatformEnvironment>& env);
|
||||
[[nodiscard]] std::unique_ptr<IPlatformUiContext> CreatePlatformUiContext();
|
||||
|
||||
[[nodiscard]] InGameConsole& GetInGameConsole();
|
||||
} // namespace OpenRCT2::Ui
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "WindowManager.h"
|
||||
|
||||
#include "interface/Theme.h"
|
||||
#include "ride/VehicleSounds.h"
|
||||
#include "windows/Window.h"
|
||||
|
||||
#include <openrct2-ui/input/InputManager.h>
|
||||
|
@ -28,6 +29,7 @@
|
|||
#include <openrct2/ui/WindowManager.h>
|
||||
|
||||
using namespace OpenRCT2::Ui;
|
||||
using namespace OpenRCT2::Ui::Windows;
|
||||
|
||||
class WindowManager final : public IWindowManager
|
||||
{
|
||||
|
@ -43,101 +45,101 @@ public:
|
|||
switch (wc)
|
||||
{
|
||||
case WindowClass::About:
|
||||
return WindowAboutOpen();
|
||||
return AboutOpen();
|
||||
case WindowClass::BottomToolbar:
|
||||
return WindowGameBottomToolbarOpen();
|
||||
return GameBottomToolbarOpen();
|
||||
case WindowClass::Changelog:
|
||||
return OpenView(WV_CHANGELOG);
|
||||
case WindowClass::Cheats:
|
||||
return WindowCheatsOpen();
|
||||
return CheatsOpen();
|
||||
case WindowClass::ClearScenery:
|
||||
return WindowClearSceneryOpen();
|
||||
return ClearSceneryOpen();
|
||||
case WindowClass::CustomCurrencyConfig:
|
||||
return CustomCurrencyWindowOpen();
|
||||
return CustomCurrencyOpen();
|
||||
case WindowClass::DebugPaint:
|
||||
return WindowDebugPaintOpen();
|
||||
return DebugPaintOpen();
|
||||
case WindowClass::EditorInventionList:
|
||||
return WindowEditorInventionsListOpen();
|
||||
return EditorInventionsListOpen();
|
||||
case WindowClass::EditorObjectSelection:
|
||||
return WindowEditorObjectSelectionOpen();
|
||||
return EditorObjectSelectionOpen();
|
||||
case WindowClass::EditorObjectiveOptions:
|
||||
return WindowEditorObjectiveOptionsOpen();
|
||||
return EditorObjectiveOptionsOpen();
|
||||
case WindowClass::EditorScenarioOptions:
|
||||
return WindowEditorScenarioOptionsOpen();
|
||||
return EditorScenarioOptionsOpen();
|
||||
case WindowClass::Finances:
|
||||
return WindowFinancesOpen();
|
||||
return FinancesOpen();
|
||||
case WindowClass::Footpath:
|
||||
return WindowFootpathOpen();
|
||||
return FootpathOpen();
|
||||
case WindowClass::GuestList:
|
||||
return WindowGuestListOpen();
|
||||
return GuestListOpen();
|
||||
case WindowClass::Land:
|
||||
return WindowLandOpen();
|
||||
return LandOpen();
|
||||
case WindowClass::LandRights:
|
||||
return WindowLandRightsOpen();
|
||||
return LandRightsOpen();
|
||||
case WindowClass::MainWindow:
|
||||
return WindowMainOpen();
|
||||
return MainOpen();
|
||||
case WindowClass::Map:
|
||||
return WindowMapOpen();
|
||||
return MapOpen();
|
||||
case WindowClass::Mapgen:
|
||||
return WindowMapgenOpen();
|
||||
return MapgenOpen();
|
||||
case WindowClass::Multiplayer:
|
||||
return WindowMultiplayerOpen();
|
||||
return MultiplayerOpen();
|
||||
case WindowClass::ConstructRide:
|
||||
return WindowNewRideOpen();
|
||||
return NewRideOpen();
|
||||
case WindowClass::ParkInformation:
|
||||
return WindowParkEntranceOpen();
|
||||
return ParkEntranceOpen();
|
||||
case WindowClass::RecentNews:
|
||||
return WindowNewsOpen();
|
||||
return NewsOpen();
|
||||
case WindowClass::RideConstruction:
|
||||
return WindowRideConstructionOpen();
|
||||
return RideConstructionOpen();
|
||||
case WindowClass::Research:
|
||||
return WindowResearchOpen();
|
||||
return ResearchOpen();
|
||||
case WindowClass::RideList:
|
||||
return WindowRideListOpen();
|
||||
return RideListOpen();
|
||||
case WindowClass::NotificationOptions:
|
||||
return WindowNewsOptionsOpen();
|
||||
return NewsOptionsOpen();
|
||||
case WindowClass::Options:
|
||||
return WindowOptionsOpen();
|
||||
return OptionsOpen();
|
||||
case WindowClass::SavePrompt:
|
||||
return WindowSavePromptOpen();
|
||||
return SavePromptOpen();
|
||||
case WindowClass::Scenery:
|
||||
return WindowSceneryOpen();
|
||||
return SceneryOpen();
|
||||
case WindowClass::SceneryScatter:
|
||||
return WindowSceneryScatterOpen();
|
||||
return SceneryScatterOpen();
|
||||
#ifndef DISABLE_NETWORK
|
||||
case WindowClass::ServerList:
|
||||
return WindowServerListOpen();
|
||||
return ServerListOpen();
|
||||
case WindowClass::ServerStart:
|
||||
return WindowServerStartOpen();
|
||||
return ServerStartOpen();
|
||||
#endif
|
||||
case WindowClass::KeyboardShortcutList:
|
||||
return WindowShortcutKeysOpen();
|
||||
return ShortcutKeysOpen();
|
||||
case WindowClass::StaffList:
|
||||
return WindowStaffListOpen();
|
||||
return StaffListOpen();
|
||||
case WindowClass::Themes:
|
||||
return WindowThemesOpen();
|
||||
return ThemesOpen();
|
||||
case WindowClass::TileInspector:
|
||||
return WindowTileInspectorOpen();
|
||||
return TileInspectorOpen();
|
||||
case WindowClass::TitleExit:
|
||||
return WindowTitleExitOpen();
|
||||
return TitleExitOpen();
|
||||
case WindowClass::TitleLogo:
|
||||
return WindowTitleLogoOpen();
|
||||
return TitleLogoOpen();
|
||||
case WindowClass::TitleMenu:
|
||||
return WindowTitleMenuOpen();
|
||||
return TitleMenuOpen();
|
||||
case WindowClass::TitleOptions:
|
||||
return WindowTitleOptionsOpen();
|
||||
return TitleOptionsOpen();
|
||||
case WindowClass::TopToolbar:
|
||||
return WindowTopToolbarOpen();
|
||||
return TopToolbarOpen();
|
||||
case WindowClass::ViewClipping:
|
||||
return WindowViewClippingOpen();
|
||||
return ViewClippingOpen();
|
||||
case WindowClass::Viewport:
|
||||
return WindowViewportOpen();
|
||||
return ViewportOpen();
|
||||
case WindowClass::Water:
|
||||
return WindowWaterOpen();
|
||||
return WaterOpen();
|
||||
case WindowClass::Transparency:
|
||||
return WindowTransparencyOpen();
|
||||
return TransparencyOpen();
|
||||
case WindowClass::AssetPacks:
|
||||
return WindowAssetPacksOpen();
|
||||
return AssetPacksOpen();
|
||||
default:
|
||||
Console::Error::WriteLine("Unhandled window class (%d)", wc);
|
||||
return nullptr;
|
||||
|
@ -149,35 +151,35 @@ public:
|
|||
switch (view)
|
||||
{
|
||||
case WV_PARK_AWARDS:
|
||||
return WindowParkAwardsOpen();
|
||||
return ParkAwardsOpen();
|
||||
case WV_PARK_RATING:
|
||||
return WindowParkRatingOpen();
|
||||
return ParkRatingOpen();
|
||||
case WV_PARK_OBJECTIVE:
|
||||
return WindowParkObjectiveOpen();
|
||||
return ParkObjectiveOpen();
|
||||
case WV_PARK_GUESTS:
|
||||
return WindowParkGuestsOpen();
|
||||
return ParkGuestsOpen();
|
||||
case WV_FINANCES_RESEARCH:
|
||||
return WindowFinancesResearchOpen();
|
||||
return FinancesResearchOpen();
|
||||
case WV_RIDE_RESEARCH:
|
||||
if (gConfigInterface.ToolbarShowResearch)
|
||||
{
|
||||
return this->OpenWindow(WindowClass::Research);
|
||||
}
|
||||
return WindowNewRideOpenResearch();
|
||||
return NewRideOpenResearch();
|
||||
case WV_MAZE_CONSTRUCTION:
|
||||
return WindowMazeConstructionOpen();
|
||||
return MazeConstructionOpen();
|
||||
case WV_NETWORK_PASSWORD:
|
||||
return WindowNetworkStatusOpenPassword();
|
||||
return NetworkStatusOpenPassword();
|
||||
case WV_EDITOR_BOTTOM_TOOLBAR:
|
||||
return WindowEditorBottomToolbarOpen();
|
||||
return EditorBottomToolbarOpen();
|
||||
case WV_CHANGELOG:
|
||||
return WindowChangelogOpen(WV_CHANGELOG);
|
||||
return ChangelogOpen(WV_CHANGELOG);
|
||||
case WV_NEW_VERSION_INFO:
|
||||
return WindowChangelogOpen(WV_NEW_VERSION_INFO);
|
||||
return ChangelogOpen(WV_NEW_VERSION_INFO);
|
||||
case WV_CONTRIBUTORS:
|
||||
return WindowChangelogOpen(WV_CONTRIBUTORS);
|
||||
return ChangelogOpen(WV_CONTRIBUTORS);
|
||||
case WV_FINANCE_MARKETING:
|
||||
return WindowFinancesMarketingOpen();
|
||||
return FinancesMarketingOpen();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -188,19 +190,19 @@ public:
|
|||
switch (type)
|
||||
{
|
||||
case WD_BANNER:
|
||||
return WindowBannerOpen(id);
|
||||
return BannerOpen(id);
|
||||
case WD_NEW_CAMPAIGN:
|
||||
return WindowNewCampaignOpen(id);
|
||||
return NewCampaignOpen(id);
|
||||
case WD_DEMOLISH_RIDE:
|
||||
return WindowRideDemolishPromptOpen(*GetRide(RideId::FromUnderlying(id)));
|
||||
return RideDemolishPromptOpen(*GetRide(RideId::FromUnderlying(id)));
|
||||
case WD_REFURBISH_RIDE:
|
||||
return WindowRideRefurbishPromptOpen(*GetRide(RideId::FromUnderlying(id)));
|
||||
return RideRefurbishPromptOpen(*GetRide(RideId::FromUnderlying(id)));
|
||||
case WD_SIGN:
|
||||
return WindowSignOpen(id);
|
||||
return SignOpen(id);
|
||||
case WD_SIGN_SMALL:
|
||||
return WindowSignSmallOpen(id);
|
||||
return SignSmallOpen(id);
|
||||
case WD_PLAYER:
|
||||
return WindowPlayerOpen(id);
|
||||
return PlayerOpen(id);
|
||||
|
||||
default:
|
||||
return nullptr;
|
||||
|
@ -209,12 +211,12 @@ public:
|
|||
|
||||
WindowBase* ShowError(StringId title, StringId message, const Formatter& args) override
|
||||
{
|
||||
return WindowErrorOpen(title, message, args);
|
||||
return ErrorOpen(title, message, args);
|
||||
}
|
||||
|
||||
WindowBase* ShowError(std::string_view title, std::string_view message) override
|
||||
{
|
||||
return WindowErrorOpen(title, message);
|
||||
return ErrorOpen(title, message);
|
||||
}
|
||||
|
||||
WindowBase* OpenIntent(Intent* intent) override
|
||||
|
@ -222,13 +224,13 @@ public:
|
|||
switch (intent->GetWindowClass())
|
||||
{
|
||||
case WindowClass::Peep:
|
||||
return WindowGuestOpen(static_cast<Peep*>(intent->GetPointerExtra(INTENT_EXTRA_PEEP)));
|
||||
return GuestOpen(static_cast<Peep*>(intent->GetPointerExtra(INTENT_EXTRA_PEEP)));
|
||||
case WindowClass::FirePrompt:
|
||||
return WindowStaffFirePromptOpen(static_cast<Peep*>(intent->GetPointerExtra(INTENT_EXTRA_PEEP)));
|
||||
return StaffFirePromptOpen(static_cast<Peep*>(intent->GetPointerExtra(INTENT_EXTRA_PEEP)));
|
||||
case WindowClass::InstallTrack:
|
||||
return WindowInstallTrackOpen(intent->GetStringExtra(INTENT_EXTRA_PATH).c_str());
|
||||
return InstallTrackOpen(intent->GetStringExtra(INTENT_EXTRA_PATH).c_str());
|
||||
case WindowClass::GuestList:
|
||||
return WindowGuestListOpenWithFilter(
|
||||
return GuestListOpenWithFilter(
|
||||
static_cast<GuestListFilterType>(intent->GetSIntExtra(INTENT_EXTRA_GUEST_LIST_FILTER)),
|
||||
intent->GetSIntExtra(INTENT_EXTRA_RIDE_ID));
|
||||
case WindowClass::Loadsave:
|
||||
|
@ -238,7 +240,7 @@ public:
|
|||
loadsave_callback callback = reinterpret_cast<loadsave_callback>(
|
||||
intent->GetPointerExtra(INTENT_EXTRA_CALLBACK));
|
||||
TrackDesign* trackDesign = static_cast<TrackDesign*>(intent->GetPointerExtra(INTENT_EXTRA_TRACK_DESIGN));
|
||||
auto* w = WindowLoadsaveOpen(
|
||||
auto* w = LoadsaveOpen(
|
||||
type, defaultName,
|
||||
[callback](int32_t result, std::string_view path) {
|
||||
if (callback != nullptr)
|
||||
|
@ -250,20 +252,19 @@ public:
|
|||
return w;
|
||||
}
|
||||
case WindowClass::ManageTrackDesign:
|
||||
return WindowTrackManageOpen(
|
||||
static_cast<TrackDesignFileRef*>(intent->GetPointerExtra(INTENT_EXTRA_TRACK_DESIGN)));
|
||||
return TrackManageOpen(static_cast<TrackDesignFileRef*>(intent->GetPointerExtra(INTENT_EXTRA_TRACK_DESIGN)));
|
||||
case WindowClass::NetworkStatus:
|
||||
{
|
||||
std::string message = intent->GetStringExtra(INTENT_EXTRA_MESSAGE);
|
||||
close_callback callback = intent->GetCloseCallbackExtra(INTENT_EXTRA_CALLBACK);
|
||||
return WindowNetworkStatusOpen(message, callback);
|
||||
return NetworkStatusOpen(message, callback);
|
||||
}
|
||||
case WindowClass::ObjectLoadError:
|
||||
{
|
||||
std::string path = intent->GetStringExtra(INTENT_EXTRA_PATH);
|
||||
auto objects = static_cast<const ObjectEntryDescriptor*>(intent->GetPointerExtra(INTENT_EXTRA_LIST));
|
||||
size_t count = intent->GetUIntExtra(INTENT_EXTRA_LIST_COUNT);
|
||||
WindowObjectLoadErrorOpen(const_cast<utf8*>(path.c_str()), count, objects);
|
||||
ObjectLoadErrorOpen(const_cast<utf8*>(path.c_str()), count, objects);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -271,20 +272,19 @@ public:
|
|||
{
|
||||
const auto rideId = RideId::FromUnderlying(intent->GetSIntExtra(INTENT_EXTRA_RIDE_ID));
|
||||
auto ride = GetRide(rideId);
|
||||
return ride == nullptr ? nullptr : WindowRideMainOpen(*ride);
|
||||
return ride == nullptr ? nullptr : RideMainOpen(*ride);
|
||||
}
|
||||
case WindowClass::TrackDesignPlace:
|
||||
return WindowTrackPlaceOpen(
|
||||
static_cast<TrackDesignFileRef*>(intent->GetPointerExtra(INTENT_EXTRA_TRACK_DESIGN)));
|
||||
return TrackPlaceOpen(static_cast<TrackDesignFileRef*>(intent->GetPointerExtra(INTENT_EXTRA_TRACK_DESIGN)));
|
||||
case WindowClass::TrackDesignList:
|
||||
{
|
||||
RideSelection rideItem;
|
||||
rideItem.Type = intent->GetUIntExtra(INTENT_EXTRA_RIDE_TYPE);
|
||||
rideItem.EntryIndex = intent->GetUIntExtra(INTENT_EXTRA_RIDE_ENTRY_INDEX);
|
||||
return WindowTrackListOpen(rideItem);
|
||||
return TrackListOpen(rideItem);
|
||||
}
|
||||
case WindowClass::ScenarioSelect:
|
||||
return WindowScenarioselectOpen(
|
||||
return ScenarioselectOpen(
|
||||
reinterpret_cast<scenarioselect_callback>(intent->GetPointerExtra(INTENT_EXTRA_CALLBACK)));
|
||||
|
||||
case WindowClass::Null:
|
||||
|
@ -301,7 +301,7 @@ public:
|
|||
case INTENT_ACTION_NEW_RIDE_OF_TYPE:
|
||||
{
|
||||
// Open ride list window
|
||||
auto w = WindowNewRideOpen();
|
||||
auto w = NewRideOpen();
|
||||
|
||||
// Switch to right tab and scroll to ride location
|
||||
RideSelection rideItem;
|
||||
|
@ -324,7 +324,7 @@ public:
|
|||
if (!ToolSet(*tlbrWindow, WC_TOP_TOOLBAR__WIDX_SCENERY, Tool::Arrow))
|
||||
{
|
||||
InputSetFlag(INPUT_FLAG_6, true);
|
||||
window = WindowSceneryOpen();
|
||||
window = SceneryOpen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -346,9 +346,9 @@ public:
|
|||
switch (intent->GetWindowDetail())
|
||||
{
|
||||
case WD_VEHICLE:
|
||||
return WindowRideOpenVehicle(static_cast<Vehicle*>(intent->GetPointerExtra(INTENT_EXTRA_VEHICLE)));
|
||||
return RideOpenVehicle(static_cast<Vehicle*>(intent->GetPointerExtra(INTENT_EXTRA_VEHICLE)));
|
||||
case WD_TRACK:
|
||||
return WindowRideOpenTrack(static_cast<TileElement*>(intent->GetPointerExtra(INTENT_EXTRA_TILE_ELEMENT)));
|
||||
return RideOpenTrack(static_cast<TileElement*>(intent->GetPointerExtra(INTENT_EXTRA_TILE_ELEMENT)));
|
||||
|
||||
case WD_NULL:
|
||||
// Intent does not hold an window detail
|
||||
|
@ -511,6 +511,10 @@ public:
|
|||
WindowInvalidateByClass(WindowClass::Research);
|
||||
break;
|
||||
|
||||
case INTENT_ACTION_UPDATE_VEHICLE_SOUNDS:
|
||||
OpenRCT2::Audio::UpdateVehicleSounds();
|
||||
break;
|
||||
|
||||
case INTENT_ACTION_TRACK_DESIGN_REMOVE_PROVISIONAL:
|
||||
TrackPlaceClearProvisionalTemporarily();
|
||||
break;
|
||||
|
@ -587,7 +591,7 @@ public:
|
|||
mainWindow->viewport_target_sprite = EntityId::GetNull();
|
||||
mainWindow->savedViewPos = viewPos;
|
||||
viewport->zoom = zoom;
|
||||
gCurrentRotation = rotation;
|
||||
viewport->rotation = rotation;
|
||||
|
||||
if (zoomDifference != ZoomLevel{ 0 })
|
||||
{
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace OpenRCT2::Audio
|
|||
{
|
||||
if (SDL_Init(SDL_INIT_AUDIO) < 0)
|
||||
{
|
||||
SDLException::Throw("SDL_Init(SDL_INIT_AUDIO)");
|
||||
Ui::SDLException::Throw("SDL_Init(SDL_INIT_AUDIO)");
|
||||
}
|
||||
_audioMixer = std::make_unique<AudioMixer>();
|
||||
}
|
||||
|
|
|
@ -15,90 +15,93 @@
|
|||
#include <openrct2/core/Imaging.h>
|
||||
#include <stdexcept>
|
||||
|
||||
static std::vector<uint8_t> ReadToVector(std::istream& stream)
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
std::vector<uint8_t> result;
|
||||
if (!stream.eof() && !stream.fail())
|
||||
static std::vector<uint8_t> ReadToVector(std::istream& stream)
|
||||
{
|
||||
stream.seekg(0, std::ios_base::end);
|
||||
auto size = stream.tellg();
|
||||
result.resize(size);
|
||||
stream.seekg(0, std::ios_base::beg);
|
||||
stream.read(reinterpret_cast<char*>(result.data()), size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO Bitmaps aren't very complicated to read so we should probably just write our
|
||||
// own implementation in libopenrct2 and spare the AOT implementation registration.
|
||||
static Image ReadBitmap(std::istream& istream, IMAGE_FORMAT format)
|
||||
{
|
||||
auto buffer = ReadToVector(istream);
|
||||
auto sdlStream = SDL_RWFromConstMem(buffer.data(), static_cast<int>(buffer.size()));
|
||||
auto bitmap = SDL_LoadBMP_RW(sdlStream, 1);
|
||||
if (bitmap != nullptr)
|
||||
{
|
||||
auto numChannels = bitmap->format->BytesPerPixel;
|
||||
if (numChannels < 3 || bitmap->format->BitsPerPixel < 24)
|
||||
std::vector<uint8_t> result;
|
||||
if (!stream.eof() && !stream.fail())
|
||||
{
|
||||
SDL_FreeSurface(bitmap);
|
||||
throw std::runtime_error("Only 24-bit bitmaps are supported.");
|
||||
stream.seekg(0, std::ios_base::end);
|
||||
auto size = stream.tellg();
|
||||
result.resize(size);
|
||||
stream.seekg(0, std::ios_base::beg);
|
||||
stream.read(reinterpret_cast<char*>(result.data()), size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Copy pixels over, then discard the surface
|
||||
if (SDL_LockSurface(bitmap) == 0)
|
||||
// TODO Bitmaps aren't very complicated to read so we should probably just write our
|
||||
// own implementation in libopenrct2 and spare the AOT implementation registration.
|
||||
static Image ReadBitmap(std::istream& istream, IMAGE_FORMAT format)
|
||||
{
|
||||
auto buffer = ReadToVector(istream);
|
||||
auto sdlStream = SDL_RWFromConstMem(buffer.data(), static_cast<int>(buffer.size()));
|
||||
auto bitmap = SDL_LoadBMP_RW(sdlStream, 1);
|
||||
if (bitmap != nullptr)
|
||||
{
|
||||
Image image;
|
||||
image.Width = bitmap->w;
|
||||
image.Height = bitmap->h;
|
||||
image.Depth = 32;
|
||||
image.Pixels.resize(bitmap->w * bitmap->h * 4);
|
||||
image.Stride = bitmap->w * 4;
|
||||
|
||||
// Clear image with 0xFF
|
||||
std::fill(image.Pixels.begin(), image.Pixels.end(), 0xFF);
|
||||
|
||||
// Copy pixels over
|
||||
auto src = static_cast<const uint8_t*>(bitmap->pixels);
|
||||
auto dst = image.Pixels.data();
|
||||
if (numChannels == 4)
|
||||
auto numChannels = bitmap->format->BytesPerPixel;
|
||||
if (numChannels < 3 || bitmap->format->BitsPerPixel < 24)
|
||||
{
|
||||
for (int32_t y = 0; y < bitmap->h; y++)
|
||||
{
|
||||
std::memcpy(dst, src, bitmap->w);
|
||||
src += bitmap->pitch;
|
||||
dst += bitmap->w;
|
||||
}
|
||||
SDL_FreeSurface(bitmap);
|
||||
throw std::runtime_error("Only 24-bit bitmaps are supported.");
|
||||
}
|
||||
else
|
||||
|
||||
// Copy pixels over, then discard the surface
|
||||
if (SDL_LockSurface(bitmap) == 0)
|
||||
{
|
||||
for (int32_t y = 0; y < bitmap->h; y++)
|
||||
Image image;
|
||||
image.Width = bitmap->w;
|
||||
image.Height = bitmap->h;
|
||||
image.Depth = 32;
|
||||
image.Pixels.resize(bitmap->w * bitmap->h * 4);
|
||||
image.Stride = bitmap->w * 4;
|
||||
|
||||
// Clear image with 0xFF
|
||||
std::fill(image.Pixels.begin(), image.Pixels.end(), 0xFF);
|
||||
|
||||
// Copy pixels over
|
||||
auto src = static_cast<const uint8_t*>(bitmap->pixels);
|
||||
auto dst = image.Pixels.data();
|
||||
if (numChannels == 4)
|
||||
{
|
||||
for (int32_t x = 0; x < bitmap->w; x++)
|
||||
for (int32_t y = 0; y < bitmap->h; y++)
|
||||
{
|
||||
std::memcpy(dst, src, 3);
|
||||
src += 3;
|
||||
dst += 4;
|
||||
std::memcpy(dst, src, bitmap->w);
|
||||
src += bitmap->pitch;
|
||||
dst += bitmap->w;
|
||||
}
|
||||
src += bitmap->pitch - (bitmap->w * 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int32_t y = 0; y < bitmap->h; y++)
|
||||
{
|
||||
for (int32_t x = 0; x < bitmap->w; x++)
|
||||
{
|
||||
std::memcpy(dst, src, 3);
|
||||
src += 3;
|
||||
dst += 4;
|
||||
}
|
||||
src += bitmap->pitch - (bitmap->w * 3);
|
||||
}
|
||||
}
|
||||
SDL_UnlockSurface(bitmap);
|
||||
SDL_FreeSurface(bitmap);
|
||||
|
||||
return image;
|
||||
}
|
||||
SDL_UnlockSurface(bitmap);
|
||||
|
||||
SDL_FreeSurface(bitmap);
|
||||
|
||||
return image;
|
||||
throw std::runtime_error("Unable to lock surface.");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(SDL_GetError());
|
||||
}
|
||||
|
||||
SDL_FreeSurface(bitmap);
|
||||
throw std::runtime_error("Unable to lock surface.");
|
||||
}
|
||||
else
|
||||
|
||||
void RegisterBitmapReader()
|
||||
{
|
||||
throw std::runtime_error(SDL_GetError());
|
||||
Imaging::SetReader(IMAGE_FORMAT::BITMAP, ReadBitmap);
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterBitmapReader()
|
||||
{
|
||||
Imaging::SetReader(IMAGE_FORMAT::BITMAP, ReadBitmap);
|
||||
}
|
||||
} // namespace OpenRCT2::Ui
|
||||
|
|
|
@ -9,4 +9,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
void RegisterBitmapReader();
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
void RegisterBitmapReader();
|
||||
}
|
||||
|
|
|
@ -13,41 +13,38 @@
|
|||
#include <openrct2/common.h>
|
||||
#include <openrct2/drawing/IDrawingEngine.h>
|
||||
|
||||
namespace OpenRCT2
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
namespace Ui
|
||||
struct IUiContext;
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Drawing::IDrawingEngine> CreateSoftwareDrawingEngine(
|
||||
const std::shared_ptr<IUiContext>& uiContext);
|
||||
[[nodiscard]] std::unique_ptr<Drawing::IDrawingEngine> CreateHardwareDisplayDrawingEngine(
|
||||
const std::shared_ptr<IUiContext>& uiContext);
|
||||
#ifndef DISABLE_OPENGL
|
||||
[[nodiscard]] std::unique_ptr<Drawing::IDrawingEngine> CreateOpenGLDrawingEngine(
|
||||
const std::shared_ptr<IUiContext>& uiContext);
|
||||
#endif
|
||||
|
||||
class DrawingEngineFactory final : public Drawing::IDrawingEngineFactory
|
||||
{
|
||||
using namespace OpenRCT2::Drawing;
|
||||
|
||||
struct IUiContext;
|
||||
|
||||
[[nodiscard]] std::unique_ptr<IDrawingEngine> CreateSoftwareDrawingEngine(const std::shared_ptr<IUiContext>& uiContext);
|
||||
[[nodiscard]] std::unique_ptr<IDrawingEngine> CreateHardwareDisplayDrawingEngine(
|
||||
const std::shared_ptr<IUiContext>& uiContext);
|
||||
#ifndef DISABLE_OPENGL
|
||||
[[nodiscard]] std::unique_ptr<IDrawingEngine> CreateOpenGLDrawingEngine(const std::shared_ptr<IUiContext>& uiContext);
|
||||
#endif
|
||||
|
||||
class DrawingEngineFactory final : public IDrawingEngineFactory
|
||||
public:
|
||||
[[nodiscard]] std::unique_ptr<Drawing::IDrawingEngine> Create(
|
||||
DrawingEngine type, const std::shared_ptr<IUiContext>& uiContext) override
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] std::unique_ptr<IDrawingEngine> Create(
|
||||
DrawingEngine type, const std::shared_ptr<IUiContext>& uiContext) override
|
||||
switch (type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case DrawingEngine::Software:
|
||||
return CreateSoftwareDrawingEngine(uiContext);
|
||||
case DrawingEngine::SoftwareWithHardwareDisplay:
|
||||
return CreateHardwareDisplayDrawingEngine(uiContext);
|
||||
case DrawingEngine::Software:
|
||||
return CreateSoftwareDrawingEngine(uiContext);
|
||||
case DrawingEngine::SoftwareWithHardwareDisplay:
|
||||
return CreateHardwareDisplayDrawingEngine(uiContext);
|
||||
#ifndef DISABLE_OPENGL
|
||||
case DrawingEngine::OpenGL:
|
||||
return CreateOpenGLDrawingEngine(uiContext);
|
||||
case DrawingEngine::OpenGL:
|
||||
return CreateOpenGLDrawingEngine(uiContext);
|
||||
#endif
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
} // namespace Ui
|
||||
} // namespace OpenRCT2
|
||||
}
|
||||
};
|
||||
} // namespace OpenRCT2::Ui
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
# include "ApplyPaletteShader.h"
|
||||
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct VDStruct
|
||||
|
|
|
@ -12,27 +12,30 @@
|
|||
#include "GLSLTypes.h"
|
||||
#include "OpenGLShaderProgram.h"
|
||||
|
||||
class ApplyPaletteShader final : public OpenGLShaderProgram
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
private:
|
||||
GLuint uTexture;
|
||||
GLuint uPalette;
|
||||
class ApplyPaletteShader final : public OpenGLShaderProgram
|
||||
{
|
||||
private:
|
||||
GLuint uTexture;
|
||||
GLuint uPalette;
|
||||
|
||||
GLuint vPosition;
|
||||
GLuint vTextureCoordinate;
|
||||
GLuint vPosition;
|
||||
GLuint vTextureCoordinate;
|
||||
|
||||
GLuint _vbo;
|
||||
GLuint _vao;
|
||||
GLuint _vbo;
|
||||
GLuint _vao;
|
||||
|
||||
public:
|
||||
ApplyPaletteShader();
|
||||
~ApplyPaletteShader() override;
|
||||
public:
|
||||
ApplyPaletteShader();
|
||||
~ApplyPaletteShader() override;
|
||||
|
||||
static void SetTexture(GLuint texture);
|
||||
void SetPalette(const vec4* glPalette);
|
||||
static void SetTexture(GLuint texture);
|
||||
void SetPalette(const vec4* glPalette);
|
||||
|
||||
void Draw();
|
||||
void Draw();
|
||||
|
||||
private:
|
||||
void GetLocations();
|
||||
};
|
||||
private:
|
||||
void GetLocations();
|
||||
};
|
||||
} // namespace OpenRCT2::Ui
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
# include "ApplyTransparencyShader.h"
|
||||
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct VDStruct
|
||||
|
|
|
@ -11,32 +11,34 @@
|
|||
|
||||
#include "GLSLTypes.h"
|
||||
#include "OpenGLShaderProgram.h"
|
||||
|
||||
class ApplyTransparencyShader final : public OpenGLShaderProgram
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
private:
|
||||
GLuint uOpaqueTex;
|
||||
GLuint uOpaqueDepth;
|
||||
GLuint uTransparentTex;
|
||||
GLuint uTransparentDepth;
|
||||
GLuint uPaletteTex;
|
||||
GLuint uBlendPaletteTex;
|
||||
class ApplyTransparencyShader final : public OpenGLShaderProgram
|
||||
{
|
||||
private:
|
||||
GLuint uOpaqueTex;
|
||||
GLuint uOpaqueDepth;
|
||||
GLuint uTransparentTex;
|
||||
GLuint uTransparentDepth;
|
||||
GLuint uPaletteTex;
|
||||
GLuint uBlendPaletteTex;
|
||||
|
||||
GLuint vPosition;
|
||||
GLuint vTextureCoordinate;
|
||||
GLuint vPosition;
|
||||
GLuint vTextureCoordinate;
|
||||
|
||||
GLuint _vbo;
|
||||
GLuint _vao;
|
||||
GLuint _vbo;
|
||||
GLuint _vao;
|
||||
|
||||
public:
|
||||
ApplyTransparencyShader();
|
||||
~ApplyTransparencyShader() override;
|
||||
public:
|
||||
ApplyTransparencyShader();
|
||||
~ApplyTransparencyShader() override;
|
||||
|
||||
static void SetTextures(
|
||||
GLuint opaqueTex, GLuint opaqueDepth, GLuint transparentTex, GLuint transparentDepth, GLuint paletteTex,
|
||||
GLuint blendPaletteTex);
|
||||
void Draw();
|
||||
static void SetTextures(
|
||||
GLuint opaqueTex, GLuint opaqueDepth, GLuint transparentTex, GLuint transparentDepth, GLuint paletteTex,
|
||||
GLuint blendPaletteTex);
|
||||
void Draw();
|
||||
|
||||
private:
|
||||
void GetLocations();
|
||||
};
|
||||
private:
|
||||
void GetLocations();
|
||||
};
|
||||
} // namespace OpenRCT2::Ui
|
||||
|
|
|
@ -15,114 +15,116 @@
|
|||
|
||||
#include <openrct2/common.h>
|
||||
#include <vector>
|
||||
|
||||
template<typename T> class CommandBatch
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
private:
|
||||
std::vector<T> _instances;
|
||||
size_t _numInstances;
|
||||
template<typename T> class CommandBatch
|
||||
{
|
||||
private:
|
||||
std::vector<T> _instances;
|
||||
size_t _numInstances;
|
||||
|
||||
public:
|
||||
CommandBatch()
|
||||
: _numInstances(0)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] bool empty() const // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return _numInstances == 0;
|
||||
}
|
||||
void clear() // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
_numInstances = 0;
|
||||
}
|
||||
T& allocate() // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
if (_numInstances + 1 > _instances.size())
|
||||
public:
|
||||
CommandBatch()
|
||||
: _numInstances(0)
|
||||
{
|
||||
_instances.resize((_numInstances + 1) << 1);
|
||||
}
|
||||
return _instances[_numInstances++];
|
||||
}
|
||||
T& insert(const T& value) // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
if (_numInstances + 1 > _instances.size())
|
||||
|
||||
[[nodiscard]] bool empty() const // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
_instances.resize((_numInstances + 1) << 1);
|
||||
return _numInstances == 0;
|
||||
}
|
||||
void clear() // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
_numInstances = 0;
|
||||
}
|
||||
T& allocate() // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
if (_numInstances + 1 > _instances.size())
|
||||
{
|
||||
_instances.resize((_numInstances + 1) << 1);
|
||||
}
|
||||
return _instances[_numInstances++];
|
||||
}
|
||||
T& insert(const T& value) // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
if (_numInstances + 1 > _instances.size())
|
||||
{
|
||||
_instances.resize((_numInstances + 1) << 1);
|
||||
}
|
||||
return _instances[_numInstances++] = value;
|
||||
}
|
||||
[[nodiscard]] size_t size() const // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return _numInstances;
|
||||
}
|
||||
const T* data() const // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return _instances.data();
|
||||
}
|
||||
const T& operator[](size_t idx) const
|
||||
{
|
||||
return _instances.at(idx);
|
||||
}
|
||||
return _instances[_numInstances++] = value;
|
||||
}
|
||||
[[nodiscard]] size_t size() const // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return _numInstances;
|
||||
}
|
||||
const T* data() const // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return _instances.data();
|
||||
}
|
||||
const T& operator[](size_t idx) const
|
||||
{
|
||||
return _instances.at(idx);
|
||||
}
|
||||
|
||||
typename std::vector<T>::iterator begin() // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return _instances.begin();
|
||||
}
|
||||
typename std::vector<T>::const_iterator begin() const // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return _instances.cbegin();
|
||||
}
|
||||
typename std::vector<T>::const_iterator cbegin() const // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return _instances.cbegin();
|
||||
}
|
||||
typename std::vector<T>::iterator end() // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return _instances.begin() + _numInstances;
|
||||
}
|
||||
typename std::vector<T>::const_iterator end() const // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return _instances.cbegin() + _numInstances;
|
||||
}
|
||||
typename std::vector<T>::const_iterator cend() const // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return _instances.cbegin() + _numInstances;
|
||||
}
|
||||
};
|
||||
|
||||
struct DrawLineCommand
|
||||
{
|
||||
ivec4 clip;
|
||||
ivec4 bounds;
|
||||
GLuint colour;
|
||||
GLint depth;
|
||||
};
|
||||
|
||||
// Per-instance data for images
|
||||
struct DrawRectCommand
|
||||
{
|
||||
ivec4 clip;
|
||||
GLint texColourAtlas;
|
||||
vec4 texColourBounds;
|
||||
GLint texMaskAtlas;
|
||||
vec4 texMaskBounds;
|
||||
ivec3 palettes;
|
||||
GLint flags;
|
||||
GLuint colour;
|
||||
ivec4 bounds;
|
||||
GLint depth;
|
||||
|
||||
enum
|
||||
{
|
||||
FLAG_NO_TEXTURE = (1u << 2u),
|
||||
FLAG_MASK = (1u << 3u),
|
||||
FLAG_CROSS_HATCH = (1u << 4u),
|
||||
FLAG_TTF_TEXT = (1u << 5u),
|
||||
// bits 8 to 16 used to store hinting threshold.
|
||||
FLAG_TTF_HINTING_THRESHOLD_MASK = 0xff00
|
||||
typename std::vector<T>::iterator begin() // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return _instances.begin();
|
||||
}
|
||||
typename std::vector<T>::const_iterator begin() const // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return _instances.cbegin();
|
||||
}
|
||||
typename std::vector<T>::const_iterator cbegin() const // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return _instances.cbegin();
|
||||
}
|
||||
typename std::vector<T>::iterator end() // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return _instances.begin() + _numInstances;
|
||||
}
|
||||
typename std::vector<T>::const_iterator end() const // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return _instances.cbegin() + _numInstances;
|
||||
}
|
||||
typename std::vector<T>::const_iterator cend() const // NOLINT(readability-identifier-naming)
|
||||
{
|
||||
return _instances.cbegin() + _numInstances;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
using LineCommandBatch = CommandBatch<DrawLineCommand>;
|
||||
using RectCommandBatch = CommandBatch<DrawRectCommand>;
|
||||
struct DrawLineCommand
|
||||
{
|
||||
ivec4 clip;
|
||||
ivec4 bounds;
|
||||
GLuint colour;
|
||||
GLint depth;
|
||||
};
|
||||
|
||||
// Per-instance data for images
|
||||
struct DrawRectCommand
|
||||
{
|
||||
ivec4 clip;
|
||||
GLint texColourAtlas;
|
||||
vec4 texColourBounds;
|
||||
GLint texMaskAtlas;
|
||||
vec4 texMaskBounds;
|
||||
ivec3 palettes;
|
||||
GLint flags;
|
||||
GLuint colour;
|
||||
ivec4 bounds;
|
||||
GLint depth;
|
||||
|
||||
enum
|
||||
{
|
||||
FLAG_NO_TEXTURE = (1u << 2u),
|
||||
FLAG_MASK = (1u << 3u),
|
||||
FLAG_CROSS_HATCH = (1u << 4u),
|
||||
FLAG_TTF_TEXT = (1u << 5u),
|
||||
// bits 8 to 16 used to store hinting threshold.
|
||||
FLAG_TTF_HINTING_THRESHOLD_MASK = 0xff00
|
||||
};
|
||||
};
|
||||
|
||||
using LineCommandBatch = CommandBatch<DrawLineCommand>;
|
||||
using RectCommandBatch = CommandBatch<DrawRectCommand>;
|
||||
} // namespace OpenRCT2::Ui
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
# include "OpenGLFramebuffer.h"
|
||||
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct VDStruct
|
||||
|
|
|
@ -12,30 +12,32 @@
|
|||
#include "DrawCommands.h"
|
||||
#include "GLSLTypes.h"
|
||||
#include "OpenGLShaderProgram.h"
|
||||
|
||||
class DrawLineShader final : public OpenGLShaderProgram
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
private:
|
||||
GLuint uScreenSize;
|
||||
class DrawLineShader final : public OpenGLShaderProgram
|
||||
{
|
||||
private:
|
||||
GLuint uScreenSize;
|
||||
|
||||
GLuint vClip;
|
||||
GLuint vBounds;
|
||||
GLuint vColour;
|
||||
GLuint vDepth;
|
||||
GLuint vClip;
|
||||
GLuint vBounds;
|
||||
GLuint vColour;
|
||||
GLuint vDepth;
|
||||
|
||||
GLuint vVertMat;
|
||||
GLuint vVertMat;
|
||||
|
||||
GLuint _vbo;
|
||||
GLuint _vboInstances;
|
||||
GLuint _vao;
|
||||
GLuint _vbo;
|
||||
GLuint _vboInstances;
|
||||
GLuint _vao;
|
||||
|
||||
public:
|
||||
DrawLineShader();
|
||||
~DrawLineShader() override;
|
||||
public:
|
||||
DrawLineShader();
|
||||
~DrawLineShader() override;
|
||||
|
||||
void SetScreenSize(int32_t width, int32_t height);
|
||||
void DrawInstances(const LineCommandBatch& instances);
|
||||
void SetScreenSize(int32_t width, int32_t height);
|
||||
void DrawInstances(const LineCommandBatch& instances);
|
||||
|
||||
private:
|
||||
void GetLocations();
|
||||
};
|
||||
private:
|
||||
void GetLocations();
|
||||
};
|
||||
} // namespace OpenRCT2::Ui
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
# include "DrawRectShader.h"
|
||||
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct VDStruct
|
||||
|
|
|
@ -14,49 +14,51 @@
|
|||
#include "OpenGLShaderProgram.h"
|
||||
|
||||
#include <SDL_pixels.h>
|
||||
|
||||
class DrawRectShader final : public OpenGLShaderProgram
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
private:
|
||||
GLuint uScreenSize;
|
||||
GLuint uTexture;
|
||||
GLuint uPaletteTex;
|
||||
class DrawRectShader final : public OpenGLShaderProgram
|
||||
{
|
||||
private:
|
||||
GLuint uScreenSize;
|
||||
GLuint uTexture;
|
||||
GLuint uPaletteTex;
|
||||
|
||||
GLuint uPeelingTex;
|
||||
GLuint uPeeling;
|
||||
GLuint uPeelingTex;
|
||||
GLuint uPeeling;
|
||||
|
||||
GLuint vVertMat;
|
||||
GLuint vVertVec;
|
||||
GLuint vVertMat;
|
||||
GLuint vVertVec;
|
||||
|
||||
GLuint vClip;
|
||||
GLuint vTexColourAtlas;
|
||||
GLuint vTexColourBounds;
|
||||
GLuint vTexMaskAtlas;
|
||||
GLuint vTexMaskBounds;
|
||||
GLuint vPalettes;
|
||||
GLuint vFlags;
|
||||
GLuint vColour;
|
||||
GLuint vBounds;
|
||||
GLuint vDepth;
|
||||
GLuint vClip;
|
||||
GLuint vTexColourAtlas;
|
||||
GLuint vTexColourBounds;
|
||||
GLuint vTexMaskAtlas;
|
||||
GLuint vTexMaskBounds;
|
||||
GLuint vPalettes;
|
||||
GLuint vFlags;
|
||||
GLuint vColour;
|
||||
GLuint vBounds;
|
||||
GLuint vDepth;
|
||||
|
||||
GLuint _vbo;
|
||||
GLuint _vboInstances;
|
||||
GLuint _vao;
|
||||
GLuint _vbo;
|
||||
GLuint _vboInstances;
|
||||
GLuint _vao;
|
||||
|
||||
GLsizei _instanceCount = 0;
|
||||
size_t _maxInstancesBufferSize;
|
||||
GLsizei _instanceCount = 0;
|
||||
size_t _maxInstancesBufferSize;
|
||||
|
||||
public:
|
||||
DrawRectShader();
|
||||
~DrawRectShader() override;
|
||||
public:
|
||||
DrawRectShader();
|
||||
~DrawRectShader() override;
|
||||
|
||||
void SetScreenSize(int32_t width, int32_t height);
|
||||
void EnablePeeling(GLuint peelingTex);
|
||||
void DisablePeeling();
|
||||
void SetScreenSize(int32_t width, int32_t height);
|
||||
void EnablePeeling(GLuint peelingTex);
|
||||
void DisablePeeling();
|
||||
|
||||
void SetInstances(const RectCommandBatch& instances);
|
||||
void DrawInstances();
|
||||
void SetInstances(const RectCommandBatch& instances);
|
||||
void DrawInstances();
|
||||
|
||||
private:
|
||||
void GetLocations();
|
||||
};
|
||||
private:
|
||||
void GetLocations();
|
||||
};
|
||||
} // namespace OpenRCT2::Ui
|
||||
|
|
|
@ -13,100 +13,102 @@
|
|||
|
||||
#include <openrct2/common.h>
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
namespace detail
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
template<typename T_> struct Vec2
|
||||
#pragma pack(push, 1)
|
||||
namespace detail
|
||||
{
|
||||
using ValueType = T_;
|
||||
|
||||
union
|
||||
template<typename T_> struct Vec2
|
||||
{
|
||||
ValueType x;
|
||||
ValueType s;
|
||||
ValueType r;
|
||||
using ValueType = T_;
|
||||
|
||||
union
|
||||
{
|
||||
ValueType x;
|
||||
ValueType s;
|
||||
ValueType r;
|
||||
};
|
||||
union
|
||||
{
|
||||
ValueType y;
|
||||
ValueType t;
|
||||
ValueType g;
|
||||
};
|
||||
};
|
||||
union
|
||||
|
||||
template struct Vec2<GLfloat>;
|
||||
template struct Vec2<GLint>;
|
||||
|
||||
template<typename T_> struct Vec3
|
||||
{
|
||||
ValueType y;
|
||||
ValueType t;
|
||||
ValueType g;
|
||||
using ValueType = T_;
|
||||
|
||||
union
|
||||
{
|
||||
ValueType x;
|
||||
ValueType s;
|
||||
ValueType r;
|
||||
};
|
||||
union
|
||||
{
|
||||
ValueType y;
|
||||
ValueType t;
|
||||
ValueType g;
|
||||
};
|
||||
union
|
||||
{
|
||||
ValueType z;
|
||||
ValueType p;
|
||||
ValueType b;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
template struct Vec2<GLfloat>;
|
||||
template struct Vec2<GLint>;
|
||||
template struct Vec3<GLfloat>;
|
||||
template struct Vec3<GLint>;
|
||||
|
||||
template<typename T_> struct Vec3
|
||||
{
|
||||
using ValueType = T_;
|
||||
|
||||
union
|
||||
template<typename T_> struct Vec4
|
||||
{
|
||||
ValueType x;
|
||||
ValueType s;
|
||||
ValueType r;
|
||||
using ValueType = T_;
|
||||
|
||||
union
|
||||
{
|
||||
ValueType x;
|
||||
ValueType s;
|
||||
ValueType r;
|
||||
};
|
||||
union
|
||||
{
|
||||
ValueType y;
|
||||
ValueType t;
|
||||
ValueType g;
|
||||
};
|
||||
union
|
||||
{
|
||||
ValueType z;
|
||||
ValueType p;
|
||||
ValueType b;
|
||||
};
|
||||
union
|
||||
{
|
||||
ValueType w;
|
||||
ValueType q;
|
||||
ValueType a;
|
||||
};
|
||||
};
|
||||
union
|
||||
{
|
||||
ValueType y;
|
||||
ValueType t;
|
||||
ValueType g;
|
||||
};
|
||||
union
|
||||
{
|
||||
ValueType z;
|
||||
ValueType p;
|
||||
ValueType b;
|
||||
};
|
||||
};
|
||||
|
||||
template struct Vec3<GLfloat>;
|
||||
template struct Vec3<GLint>;
|
||||
template struct Vec4<GLfloat>;
|
||||
template struct Vec4<GLint>;
|
||||
|
||||
template<typename T_> struct Vec4
|
||||
{
|
||||
using ValueType = T_;
|
||||
} // namespace detail
|
||||
|
||||
union
|
||||
{
|
||||
ValueType x;
|
||||
ValueType s;
|
||||
ValueType r;
|
||||
};
|
||||
union
|
||||
{
|
||||
ValueType y;
|
||||
ValueType t;
|
||||
ValueType g;
|
||||
};
|
||||
union
|
||||
{
|
||||
ValueType z;
|
||||
ValueType p;
|
||||
ValueType b;
|
||||
};
|
||||
union
|
||||
{
|
||||
ValueType w;
|
||||
ValueType q;
|
||||
ValueType a;
|
||||
};
|
||||
};
|
||||
using vec2 = detail::Vec2<GLfloat>;
|
||||
using ivec2 = detail::Vec2<GLint>;
|
||||
|
||||
template struct Vec4<GLfloat>;
|
||||
template struct Vec4<GLint>;
|
||||
using vec3 = detail::Vec3<GLfloat>;
|
||||
using ivec3 = detail::Vec3<GLint>;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
using vec2 = detail::Vec2<GLfloat>;
|
||||
using ivec2 = detail::Vec2<GLint>;
|
||||
|
||||
using vec3 = detail::Vec3<GLfloat>;
|
||||
using ivec3 = detail::Vec3<GLint>;
|
||||
|
||||
using vec4 = detail::Vec4<GLfloat>;
|
||||
using ivec4 = detail::Vec4<GLint>;
|
||||
using vec4 = detail::Vec4<GLfloat>;
|
||||
using ivec4 = detail::Vec4<GLint>;
|
||||
|
||||
#pragma pack(pop)
|
||||
} // namespace OpenRCT2::Ui
|
||||
|
|
|
@ -38,7 +38,9 @@ static const char* TryLoadAllProcAddresses()
|
|||
|
||||
# endif /* #if OPENGL_NO_LINK */
|
||||
|
||||
namespace OpenGLState
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
namespace OpenRCT2::Ui::OpenGLState
|
||||
{
|
||||
uint16_t ActiveTexture;
|
||||
GLuint CurrentProgram;
|
||||
|
@ -48,7 +50,7 @@ namespace OpenGLState
|
|||
ActiveTexture = UINT16_MAX;
|
||||
CurrentProgram = UINT32_MAX;
|
||||
}
|
||||
} // namespace OpenGLState
|
||||
} // namespace OpenRCT2::Ui::OpenGLState
|
||||
|
||||
void OpenGLAPI::SetTexture(uint16_t index, GLenum type, GLuint texture)
|
||||
{
|
||||
|
|
|
@ -113,26 +113,29 @@ using PFNGLGETTEXIMAGEPROC = void(APIENTRYP)(GLenum target, GLint level, GLenum
|
|||
|
||||
#endif /* OPENGL_NO_LINK */
|
||||
|
||||
inline void CheckGLError()
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
GLenum error = glGetError();
|
||||
while (error != GL_NO_ERROR)
|
||||
inline void CheckGLError()
|
||||
{
|
||||
LOG_ERROR("OpenGL Error 0x%04X", error);
|
||||
error = glGetError();
|
||||
GLenum error = glGetError();
|
||||
while (error != GL_NO_ERROR)
|
||||
{
|
||||
LOG_ERROR("OpenGL Error 0x%04X", error);
|
||||
error = glGetError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace OpenGLAPI
|
||||
{
|
||||
bool Initialise();
|
||||
void SetTexture(uint16_t index, GLenum type, GLuint texture);
|
||||
} // namespace OpenGLAPI
|
||||
namespace OpenGLAPI
|
||||
{
|
||||
bool Initialise();
|
||||
void SetTexture(uint16_t index, GLenum type, GLuint texture);
|
||||
} // namespace OpenGLAPI
|
||||
|
||||
namespace OpenGLState
|
||||
{
|
||||
extern uint16_t ActiveTexture;
|
||||
extern GLuint CurrentProgram;
|
||||
namespace OpenGLState
|
||||
{
|
||||
extern uint16_t ActiveTexture;
|
||||
extern GLuint CurrentProgram;
|
||||
|
||||
void Reset();
|
||||
} // namespace OpenGLState
|
||||
void Reset();
|
||||
} // namespace OpenGLState
|
||||
} // namespace OpenRCT2::Ui
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
# include <openrct2/ui/UiContext.h>
|
||||
# include <openrct2/util/Util.h>
|
||||
# include <openrct2/world/Climate.h>
|
||||
# include <unordered_map>
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Drawing;
|
||||
|
@ -74,6 +73,8 @@ private:
|
|||
|
||||
int32_t _drawCount = 0;
|
||||
|
||||
uint32_t _ttfGlId = 0;
|
||||
|
||||
struct
|
||||
{
|
||||
LineCommandBatch lines;
|
||||
|
@ -99,26 +100,25 @@ public:
|
|||
void ResetPalette();
|
||||
void StartNewDraw();
|
||||
|
||||
void Clear(DrawPixelInfo* dpi, uint8_t paletteIndex) override;
|
||||
void FillRect(DrawPixelInfo* dpi, uint32_t colour, int32_t x, int32_t y, int32_t w, int32_t h) override;
|
||||
void Clear(DrawPixelInfo& dpi, uint8_t paletteIndex) override;
|
||||
void FillRect(DrawPixelInfo& dpi, uint32_t colour, int32_t x, int32_t y, int32_t w, int32_t h) override;
|
||||
void FilterRect(
|
||||
DrawPixelInfo* dpi, FilterPaletteID palette, int32_t left, int32_t top, int32_t right, int32_t bottom) override;
|
||||
void DrawLine(DrawPixelInfo* dpi, uint32_t colour, const ScreenLine& line) override;
|
||||
void DrawSprite(DrawPixelInfo* dpi, const ImageId imageId, int32_t x, int32_t y) override;
|
||||
DrawPixelInfo& dpi, FilterPaletteID palette, int32_t left, int32_t top, int32_t right, int32_t bottom) override;
|
||||
void DrawLine(DrawPixelInfo& dpi, uint32_t colour, const ScreenLine& line) override;
|
||||
void DrawSprite(DrawPixelInfo& dpi, const ImageId imageId, int32_t x, int32_t y) override;
|
||||
void DrawSpriteRawMasked(
|
||||
DrawPixelInfo* dpi, int32_t x, int32_t y, const ImageId maskImage, const ImageId colourImage) override;
|
||||
void DrawSpriteSolid(DrawPixelInfo* dpi, const ImageId image, int32_t x, int32_t y, uint8_t colour) override;
|
||||
void DrawGlyph(DrawPixelInfo* dpi, const ImageId image, int32_t x, int32_t y, const PaletteMap& palette) override;
|
||||
DrawPixelInfo& dpi, int32_t x, int32_t y, const ImageId maskImage, const ImageId colourImage) override;
|
||||
void DrawSpriteSolid(DrawPixelInfo& dpi, const ImageId image, int32_t x, int32_t y, uint8_t colour) override;
|
||||
void DrawGlyph(DrawPixelInfo& dpi, const ImageId image, int32_t x, int32_t y, const PaletteMap& palette) override;
|
||||
void DrawTTFBitmap(
|
||||
DrawPixelInfo* dpi, TextDrawInfo* info, ImageIndex image, const void* pixels, int32_t width, int32_t height, int32_t x,
|
||||
int32_t y, uint8_t hinting_threshold) override;
|
||||
DrawPixelInfo& dpi, TextDrawInfo* info, TTFSurface* surface, int32_t x, int32_t y, uint8_t hintingThreshold) override;
|
||||
|
||||
void FlushCommandBuffers();
|
||||
|
||||
void FlushLines();
|
||||
void FlushRectangles();
|
||||
void HandleTransparency();
|
||||
void CalculcateClipping(DrawPixelInfo* dpi);
|
||||
void CalculcateClipping(DrawPixelInfo& dpi);
|
||||
};
|
||||
|
||||
class OpenGLWeatherDrawer final : public IWeatherDrawer
|
||||
|
@ -161,7 +161,7 @@ public:
|
|||
int32_t pixelX = xPixelOffset % dpi.width;
|
||||
int32_t pixelY = (xPixelOffset / dpi.width) % dpi.height;
|
||||
|
||||
_drawingContext->DrawLine(&dpi, patternPixel, { { pixelX, pixelY }, { pixelX + 1, pixelY + 1 } });
|
||||
_drawingContext->DrawLine(dpi, patternPixel, { { pixelX, pixelY }, { pixelX + 1, pixelY + 1 } });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -285,7 +285,7 @@ public:
|
|||
assert(_screenFramebuffer != nullptr);
|
||||
|
||||
_drawingContext->StartNewDraw();
|
||||
_drawingContext->CalculcateClipping(&_bitsDPI);
|
||||
_drawingContext->CalculcateClipping(_bitsDPI);
|
||||
}
|
||||
|
||||
void EndDraw() override
|
||||
|
@ -323,7 +323,7 @@ public:
|
|||
|
||||
void PaintWindows() override
|
||||
{
|
||||
_drawingContext->CalculcateClipping(&_bitsDPI);
|
||||
_drawingContext->CalculcateClipping(_bitsDPI);
|
||||
|
||||
WindowUpdateAllViewports();
|
||||
WindowDrawAll(_bitsDPI, 0, 0, _width, _height);
|
||||
|
@ -331,7 +331,7 @@ public:
|
|||
|
||||
void PaintWeather() override
|
||||
{
|
||||
_drawingContext->CalculcateClipping(&_bitsDPI);
|
||||
_drawingContext->CalculcateClipping(_bitsDPI);
|
||||
|
||||
DrawWeather(_bitsDPI, &_weatherDrawer);
|
||||
}
|
||||
|
@ -509,7 +509,7 @@ void OpenGLDrawingContext::StartNewDraw()
|
|||
_swapFramebuffer->Clear();
|
||||
}
|
||||
|
||||
void OpenGLDrawingContext::Clear(DrawPixelInfo* dpi, uint8_t paletteIndex)
|
||||
void OpenGLDrawingContext::Clear(DrawPixelInfo& dpi, uint8_t paletteIndex)
|
||||
{
|
||||
CalculcateClipping(dpi);
|
||||
|
||||
|
@ -517,7 +517,7 @@ void OpenGLDrawingContext::Clear(DrawPixelInfo* dpi, uint8_t paletteIndex)
|
|||
}
|
||||
|
||||
void OpenGLDrawingContext::FillRect(
|
||||
DrawPixelInfo* dpi, uint32_t colour, int32_t left, int32_t top, int32_t right, int32_t bottom)
|
||||
DrawPixelInfo& dpi, uint32_t colour, int32_t left, int32_t top, int32_t right, int32_t bottom)
|
||||
{
|
||||
CalculcateClipping(dpi);
|
||||
|
||||
|
@ -552,7 +552,7 @@ void OpenGLDrawingContext::FillRect(
|
|||
}
|
||||
|
||||
void OpenGLDrawingContext::FilterRect(
|
||||
DrawPixelInfo* dpi, FilterPaletteID palette, int32_t left, int32_t top, int32_t right, int32_t bottom)
|
||||
DrawPixelInfo& dpi, FilterPaletteID palette, int32_t left, int32_t top, int32_t right, int32_t bottom)
|
||||
{
|
||||
CalculcateClipping(dpi);
|
||||
|
||||
|
@ -575,7 +575,7 @@ void OpenGLDrawingContext::FilterRect(
|
|||
command.depth = _drawCount++;
|
||||
}
|
||||
|
||||
void OpenGLDrawingContext::DrawLine(DrawPixelInfo* dpi, uint32_t colour, const ScreenLine& line)
|
||||
void OpenGLDrawingContext::DrawLine(DrawPixelInfo& dpi, uint32_t colour, const ScreenLine& line)
|
||||
{
|
||||
CalculcateClipping(dpi);
|
||||
|
||||
|
@ -587,7 +587,7 @@ void OpenGLDrawingContext::DrawLine(DrawPixelInfo* dpi, uint32_t colour, const S
|
|||
command.depth = _drawCount++;
|
||||
}
|
||||
|
||||
void OpenGLDrawingContext::DrawSprite(DrawPixelInfo* dpi, const ImageId imageId, int32_t x, int32_t y)
|
||||
void OpenGLDrawingContext::DrawSprite(DrawPixelInfo& dpi, const ImageId imageId, int32_t x, int32_t y)
|
||||
{
|
||||
CalculcateClipping(dpi);
|
||||
|
||||
|
@ -597,19 +597,19 @@ void OpenGLDrawingContext::DrawSprite(DrawPixelInfo* dpi, const ImageId imageId,
|
|||
return;
|
||||
}
|
||||
|
||||
if (dpi->zoom_level > ZoomLevel{ 0 })
|
||||
if (dpi.zoom_level > ZoomLevel{ 0 })
|
||||
{
|
||||
if (g1Element->flags & G1_FLAG_HAS_ZOOM_SPRITE)
|
||||
{
|
||||
DrawPixelInfo zoomedDPI;
|
||||
zoomedDPI.bits = dpi->bits;
|
||||
zoomedDPI.x = dpi->x >> 1;
|
||||
zoomedDPI.y = dpi->y >> 1;
|
||||
zoomedDPI.height = dpi->height >> 1;
|
||||
zoomedDPI.width = dpi->width >> 1;
|
||||
zoomedDPI.pitch = dpi->pitch;
|
||||
zoomedDPI.zoom_level = dpi->zoom_level - 1;
|
||||
DrawSprite(&zoomedDPI, imageId.WithIndex(imageId.GetIndex() - g1Element->zoomed_offset), x >> 1, y >> 1);
|
||||
zoomedDPI.bits = dpi.bits;
|
||||
zoomedDPI.x = dpi.x >> 1;
|
||||
zoomedDPI.y = dpi.y >> 1;
|
||||
zoomedDPI.height = dpi.height >> 1;
|
||||
zoomedDPI.width = dpi.width >> 1;
|
||||
zoomedDPI.pitch = dpi.pitch;
|
||||
zoomedDPI.zoom_level = dpi.zoom_level - 1;
|
||||
DrawSprite(zoomedDPI, imageId.WithIndex(imageId.GetIndex() - g1Element->zoomed_offset), x >> 1, y >> 1);
|
||||
return;
|
||||
}
|
||||
if (g1Element->flags & G1_FLAG_NO_ZOOM_DRAW)
|
||||
|
@ -622,11 +622,11 @@ void OpenGLDrawingContext::DrawSprite(DrawPixelInfo* dpi, const ImageId imageId,
|
|||
int32_t top = y + g1Element->y_offset;
|
||||
|
||||
int32_t zoom_mask;
|
||||
if (dpi->zoom_level >= ZoomLevel{ 0 })
|
||||
zoom_mask = dpi->zoom_level.ApplyTo(0xFFFFFFFF);
|
||||
if (dpi.zoom_level >= ZoomLevel{ 0 })
|
||||
zoom_mask = dpi.zoom_level.ApplyTo(0xFFFFFFFF);
|
||||
else
|
||||
zoom_mask = 0xFFFFFFFF;
|
||||
if (dpi->zoom_level != ZoomLevel{ 0 } && (g1Element->flags & G1_FLAG_RLE_COMPRESSION))
|
||||
if (dpi.zoom_level != ZoomLevel{ 0 } && (g1Element->flags & G1_FLAG_RLE_COMPRESSION))
|
||||
{
|
||||
top -= ~zoom_mask;
|
||||
}
|
||||
|
@ -642,7 +642,7 @@ void OpenGLDrawingContext::DrawSprite(DrawPixelInfo* dpi, const ImageId imageId,
|
|||
int32_t right = left + g1Element->width;
|
||||
int32_t bottom = top + g1Element->height;
|
||||
|
||||
if (dpi->zoom_level != ZoomLevel{ 0 } && (g1Element->flags & G1_FLAG_RLE_COMPRESSION))
|
||||
if (dpi.zoom_level != ZoomLevel{ 0 } && (g1Element->flags & G1_FLAG_RLE_COMPRESSION))
|
||||
{
|
||||
bottom += top & ~zoom_mask;
|
||||
}
|
||||
|
@ -656,15 +656,15 @@ void OpenGLDrawingContext::DrawSprite(DrawPixelInfo* dpi, const ImageId imageId,
|
|||
std::swap(top, bottom);
|
||||
}
|
||||
|
||||
left -= dpi->x;
|
||||
top -= dpi->y;
|
||||
right -= dpi->x;
|
||||
bottom -= dpi->y;
|
||||
left -= dpi.x;
|
||||
top -= dpi.y;
|
||||
right -= dpi.x;
|
||||
bottom -= dpi.y;
|
||||
|
||||
left = dpi->zoom_level.ApplyInversedTo(left);
|
||||
top = dpi->zoom_level.ApplyInversedTo(top);
|
||||
right = dpi->zoom_level.ApplyInversedTo(right);
|
||||
bottom = dpi->zoom_level.ApplyInversedTo(bottom);
|
||||
left = dpi.zoom_level.ApplyInversedTo(left);
|
||||
top = dpi.zoom_level.ApplyInversedTo(top);
|
||||
right = dpi.zoom_level.ApplyInversedTo(right);
|
||||
bottom = dpi.zoom_level.ApplyInversedTo(bottom);
|
||||
|
||||
left += _spriteOffset.x;
|
||||
top += _spriteOffset.y;
|
||||
|
@ -738,7 +738,7 @@ void OpenGLDrawingContext::DrawSprite(DrawPixelInfo* dpi, const ImageId imageId,
|
|||
}
|
||||
|
||||
void OpenGLDrawingContext::DrawSpriteRawMasked(
|
||||
DrawPixelInfo* dpi, int32_t x, int32_t y, const ImageId maskImage, const ImageId colourImage)
|
||||
DrawPixelInfo& dpi, int32_t x, int32_t y, const ImageId maskImage, const ImageId colourImage)
|
||||
{
|
||||
CalculcateClipping(dpi);
|
||||
|
||||
|
@ -771,15 +771,15 @@ void OpenGLDrawingContext::DrawSpriteRawMasked(
|
|||
std::swap(top, bottom);
|
||||
}
|
||||
|
||||
left -= dpi->x;
|
||||
top -= dpi->y;
|
||||
right -= dpi->x;
|
||||
bottom -= dpi->y;
|
||||
left -= dpi.x;
|
||||
top -= dpi.y;
|
||||
right -= dpi.x;
|
||||
bottom -= dpi.y;
|
||||
|
||||
left = dpi->zoom_level.ApplyInversedTo(left);
|
||||
top = dpi->zoom_level.ApplyInversedTo(top);
|
||||
right = dpi->zoom_level.ApplyInversedTo(right);
|
||||
bottom = dpi->zoom_level.ApplyInversedTo(bottom);
|
||||
left = dpi.zoom_level.ApplyInversedTo(left);
|
||||
top = dpi.zoom_level.ApplyInversedTo(top);
|
||||
right = dpi.zoom_level.ApplyInversedTo(right);
|
||||
bottom = dpi.zoom_level.ApplyInversedTo(bottom);
|
||||
|
||||
left += _spriteOffset.x;
|
||||
top += _spriteOffset.y;
|
||||
|
@ -800,7 +800,7 @@ void OpenGLDrawingContext::DrawSpriteRawMasked(
|
|||
command.depth = _drawCount++;
|
||||
}
|
||||
|
||||
void OpenGLDrawingContext::DrawSpriteSolid(DrawPixelInfo* dpi, const ImageId image, int32_t x, int32_t y, uint8_t colour)
|
||||
void OpenGLDrawingContext::DrawSpriteSolid(DrawPixelInfo& dpi, const ImageId image, int32_t x, int32_t y, uint8_t colour)
|
||||
{
|
||||
CalculcateClipping(dpi);
|
||||
|
||||
|
@ -852,7 +852,7 @@ void OpenGLDrawingContext::DrawSpriteSolid(DrawPixelInfo* dpi, const ImageId ima
|
|||
command.depth = _drawCount++;
|
||||
}
|
||||
|
||||
void OpenGLDrawingContext::DrawGlyph(DrawPixelInfo* dpi, const ImageId image, int32_t x, int32_t y, const PaletteMap& palette)
|
||||
void OpenGLDrawingContext::DrawGlyph(DrawPixelInfo& dpi, const ImageId image, int32_t x, int32_t y, const PaletteMap& palette)
|
||||
{
|
||||
CalculcateClipping(dpi);
|
||||
|
||||
|
@ -878,15 +878,15 @@ void OpenGLDrawingContext::DrawGlyph(DrawPixelInfo* dpi, const ImageId image, in
|
|||
std::swap(top, bottom);
|
||||
}
|
||||
|
||||
left -= dpi->x;
|
||||
top -= dpi->y;
|
||||
right -= dpi->x;
|
||||
bottom -= dpi->y;
|
||||
left -= dpi.x;
|
||||
top -= dpi.y;
|
||||
right -= dpi.x;
|
||||
bottom -= dpi.y;
|
||||
|
||||
left = dpi->zoom_level.ApplyInversedTo(left);
|
||||
top = dpi->zoom_level.ApplyInversedTo(top);
|
||||
right = dpi->zoom_level.ApplyInversedTo(right);
|
||||
bottom = dpi->zoom_level.ApplyInversedTo(bottom);
|
||||
left = dpi.zoom_level.ApplyInversedTo(left);
|
||||
top = dpi.zoom_level.ApplyInversedTo(top);
|
||||
right = dpi.zoom_level.ApplyInversedTo(right);
|
||||
bottom = dpi.zoom_level.ApplyInversedTo(bottom);
|
||||
|
||||
left += _spriteOffset.x;
|
||||
top += _spriteOffset.y;
|
||||
|
@ -908,17 +908,25 @@ void OpenGLDrawingContext::DrawGlyph(DrawPixelInfo* dpi, const ImageId image, in
|
|||
}
|
||||
|
||||
void OpenGLDrawingContext::DrawTTFBitmap(
|
||||
DrawPixelInfo* dpi, TextDrawInfo* info, ImageIndex image, const void* pixels, int32_t width, int32_t height, int32_t x,
|
||||
int32_t y, uint8_t hinting_threshold)
|
||||
DrawPixelInfo& dpi, TextDrawInfo* info, TTFSurface* surface, int32_t x, int32_t y, uint8_t hintingThreshold)
|
||||
{
|
||||
# ifndef NO_TTF
|
||||
CalculcateClipping(dpi);
|
||||
|
||||
const auto texture = _textureCache->GetOrLoadBitmapTexture(image, pixels, width, height);
|
||||
auto baseId = uint32_t(0x7FFFF) - 1024;
|
||||
auto imageId = baseId + _ttfGlId;
|
||||
_engine.InvalidateImage(imageId);
|
||||
const auto texture = _textureCache->GetOrLoadBitmapTexture(imageId, surface->pixels, surface->w, surface->h);
|
||||
_ttfGlId++;
|
||||
if (_ttfGlId >= 1023)
|
||||
{
|
||||
_ttfGlId = 0;
|
||||
}
|
||||
|
||||
int32_t drawOffsetX = 0;
|
||||
int32_t drawOffsetY = 0;
|
||||
int32_t drawWidth = static_cast<uint16_t>(width);
|
||||
int32_t drawHeight = static_cast<uint16_t>(height);
|
||||
int32_t drawWidth = static_cast<uint16_t>(surface->w);
|
||||
int32_t drawHeight = static_cast<uint16_t>(surface->h);
|
||||
|
||||
int32_t left = x + drawOffsetX;
|
||||
int32_t top = y + drawOffsetY;
|
||||
|
@ -976,7 +984,7 @@ void OpenGLDrawingContext::DrawTTFBitmap(
|
|||
command.bounds = { left + 1, top + 1, right + 1, bottom + 1 };
|
||||
command.depth = _drawCount++;
|
||||
}
|
||||
auto& cmdBuf = hinting_threshold > 0 ? _commandBuffers.transparent : _commandBuffers.rects;
|
||||
auto& cmdBuf = hintingThreshold > 0 ? _commandBuffers.transparent : _commandBuffers.rects;
|
||||
DrawRectCommand& command = cmdBuf.allocate();
|
||||
command.clip = { _clipLeft, _clipTop, _clipRight, _clipBottom };
|
||||
command.texColourAtlas = texture.index;
|
||||
|
@ -984,10 +992,11 @@ void OpenGLDrawingContext::DrawTTFBitmap(
|
|||
command.texMaskAtlas = 0;
|
||||
command.texMaskBounds = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
command.palettes = { 0, 0, 0 };
|
||||
command.flags = DrawRectCommand::FLAG_TTF_TEXT | (hinting_threshold << 8);
|
||||
command.flags = DrawRectCommand::FLAG_TTF_TEXT | (hintingThreshold << 8);
|
||||
command.colour = info->palette[1];
|
||||
command.bounds = { left, top, right, bottom };
|
||||
command.depth = _drawCount++;
|
||||
# endif // NO_TTF
|
||||
}
|
||||
|
||||
void OpenGLDrawingContext::FlushCommandBuffers()
|
||||
|
@ -1067,24 +1076,24 @@ void OpenGLDrawingContext::HandleTransparency()
|
|||
_commandBuffers.transparent.clear();
|
||||
}
|
||||
|
||||
void OpenGLDrawingContext::CalculcateClipping(DrawPixelInfo* dpi)
|
||||
void OpenGLDrawingContext::CalculcateClipping(DrawPixelInfo& dpi)
|
||||
{
|
||||
auto screenDPI = _engine.GetDPI();
|
||||
auto bytesPerRow = screenDPI->GetBytesPerRow();
|
||||
auto bitsOffset = static_cast<size_t>(dpi->bits - screenDPI->bits);
|
||||
auto bitsOffset = static_cast<size_t>(dpi.bits - screenDPI->bits);
|
||||
# ifndef NDEBUG
|
||||
auto bitsSize = static_cast<size_t>(screenDPI->height) * bytesPerRow;
|
||||
assert(bitsOffset < bitsSize);
|
||||
# endif
|
||||
|
||||
_clipLeft = static_cast<int32_t>(bitsOffset % bytesPerRow) + dpi->remX;
|
||||
_clipTop = static_cast<int32_t>(bitsOffset / bytesPerRow) + dpi->remY;
|
||||
_clipRight = _clipLeft + dpi->zoom_level.ApplyInversedTo(dpi->width);
|
||||
_clipBottom = _clipTop + dpi->zoom_level.ApplyInversedTo(dpi->height);
|
||||
_offsetX = _clipLeft - dpi->x;
|
||||
_offsetY = _clipTop - dpi->y;
|
||||
_spriteOffset.x = _clipLeft - dpi->remX;
|
||||
_spriteOffset.y = _clipTop - dpi->remY;
|
||||
_clipLeft = static_cast<int32_t>(bitsOffset % bytesPerRow) + dpi.remX;
|
||||
_clipTop = static_cast<int32_t>(bitsOffset / bytesPerRow) + dpi.remY;
|
||||
_clipRight = _clipLeft + dpi.zoom_level.ApplyInversedTo(dpi.width);
|
||||
_clipBottom = _clipTop + dpi.zoom_level.ApplyInversedTo(dpi.height);
|
||||
_offsetX = _clipLeft - dpi.x;
|
||||
_offsetY = _clipTop - dpi.y;
|
||||
_spriteOffset.x = _clipLeft - dpi.remX;
|
||||
_spriteOffset.y = _clipTop - dpi.remY;
|
||||
}
|
||||
|
||||
#endif /* DISABLE_OPENGL */
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
# include <memory>
|
||||
# include <openrct2/common.h>
|
||||
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
constexpr GLuint BACKBUFFER_ID = 0;
|
||||
|
||||
OpenGLFramebuffer::OpenGLFramebuffer(SDL_Window* window)
|
||||
|
|
|
@ -16,49 +16,51 @@
|
|||
#include <vector>
|
||||
|
||||
struct SDL_Window;
|
||||
|
||||
class OpenGLFramebuffer
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
private:
|
||||
GLuint _id;
|
||||
GLuint _texture;
|
||||
GLuint _depth;
|
||||
int32_t _width;
|
||||
int32_t _height;
|
||||
|
||||
public:
|
||||
explicit OpenGLFramebuffer(SDL_Window* window);
|
||||
OpenGLFramebuffer(int32_t width, int32_t height, bool depth = true, bool integer = true, bool word = false);
|
||||
~OpenGLFramebuffer();
|
||||
|
||||
OpenGLFramebuffer(const OpenGLFramebuffer&) = delete;
|
||||
OpenGLFramebuffer& operator=(const OpenGLFramebuffer&) = delete;
|
||||
|
||||
GLuint GetWidth() const
|
||||
class OpenGLFramebuffer
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
GLuint GetHeight() const
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
GLuint GetTexture() const
|
||||
{
|
||||
return _texture;
|
||||
}
|
||||
GLuint GetDepthTexture() const
|
||||
{
|
||||
return _depth;
|
||||
}
|
||||
private:
|
||||
GLuint _id;
|
||||
GLuint _texture;
|
||||
GLuint _depth;
|
||||
int32_t _width;
|
||||
int32_t _height;
|
||||
|
||||
void Bind() const;
|
||||
void BindDraw() const;
|
||||
void BindRead() const;
|
||||
void GetPixels(DrawPixelInfo& dpi) const;
|
||||
public:
|
||||
explicit OpenGLFramebuffer(SDL_Window* window);
|
||||
OpenGLFramebuffer(int32_t width, int32_t height, bool depth = true, bool integer = true, bool word = false);
|
||||
~OpenGLFramebuffer();
|
||||
|
||||
void SwapColourBuffer(OpenGLFramebuffer& other);
|
||||
GLuint SwapDepthTexture(GLuint depth);
|
||||
void Copy(OpenGLFramebuffer& src, GLenum filter);
|
||||
OpenGLFramebuffer(const OpenGLFramebuffer&) = delete;
|
||||
OpenGLFramebuffer& operator=(const OpenGLFramebuffer&) = delete;
|
||||
|
||||
static GLuint CreateDepthTexture(int32_t width, int32_t height);
|
||||
};
|
||||
GLuint GetWidth() const
|
||||
{
|
||||
return _width;
|
||||
}
|
||||
GLuint GetHeight() const
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
GLuint GetTexture() const
|
||||
{
|
||||
return _texture;
|
||||
}
|
||||
GLuint GetDepthTexture() const
|
||||
{
|
||||
return _depth;
|
||||
}
|
||||
|
||||
void Bind() const;
|
||||
void BindDraw() const;
|
||||
void BindRead() const;
|
||||
void GetPixels(DrawPixelInfo& dpi) const;
|
||||
|
||||
void SwapColourBuffer(OpenGLFramebuffer& other);
|
||||
GLuint SwapDepthTexture(GLuint depth);
|
||||
void Copy(OpenGLFramebuffer& src, GLenum filter);
|
||||
|
||||
static GLuint CreateDepthTexture(int32_t width, int32_t height);
|
||||
};
|
||||
} // namespace OpenRCT2::Ui
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
# include <openrct2/core/Path.hpp>
|
||||
# include <openrct2/core/String.hpp>
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
OpenGLShader::OpenGLShader(const char* name, GLenum type)
|
||||
: _type(type)
|
||||
|
|
|
@ -14,43 +14,45 @@
|
|||
#include <memory>
|
||||
#include <openrct2/common.h>
|
||||
#include <string>
|
||||
|
||||
class OpenGLShader final
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
private:
|
||||
static constexpr uint64_t MaxSourceSize = 8 * 1024 * 1024; // 8 MiB
|
||||
class OpenGLShader final
|
||||
{
|
||||
private:
|
||||
static constexpr uint64_t MaxSourceSize = 8 * 1024 * 1024; // 8 MiB
|
||||
|
||||
GLenum _type;
|
||||
GLuint _id = 0;
|
||||
GLenum _type;
|
||||
GLuint _id = 0;
|
||||
|
||||
public:
|
||||
OpenGLShader(const char* name, GLenum type);
|
||||
~OpenGLShader();
|
||||
public:
|
||||
OpenGLShader(const char* name, GLenum type);
|
||||
~OpenGLShader();
|
||||
|
||||
GLuint GetShaderId();
|
||||
GLuint GetShaderId();
|
||||
|
||||
private:
|
||||
std::string GetPath(const std::string& name);
|
||||
static std::string ReadSourceCode(const std::string& path);
|
||||
};
|
||||
private:
|
||||
std::string GetPath(const std::string& name);
|
||||
static std::string ReadSourceCode(const std::string& path);
|
||||
};
|
||||
|
||||
class OpenGLShaderProgram
|
||||
{
|
||||
private:
|
||||
GLuint _id = 0;
|
||||
std::unique_ptr<OpenGLShader> _vertexShader;
|
||||
std::unique_ptr<OpenGLShader> _fragmentShader;
|
||||
class OpenGLShaderProgram
|
||||
{
|
||||
private:
|
||||
GLuint _id = 0;
|
||||
std::unique_ptr<OpenGLShader> _vertexShader;
|
||||
std::unique_ptr<OpenGLShader> _fragmentShader;
|
||||
|
||||
public:
|
||||
explicit OpenGLShaderProgram(const char* name);
|
||||
explicit OpenGLShaderProgram(const OpenGLShaderProgram&) = delete;
|
||||
explicit OpenGLShaderProgram(OpenGLShaderProgram&&) = default;
|
||||
virtual ~OpenGLShaderProgram();
|
||||
public:
|
||||
explicit OpenGLShaderProgram(const char* name);
|
||||
explicit OpenGLShaderProgram(const OpenGLShaderProgram&) = delete;
|
||||
explicit OpenGLShaderProgram(OpenGLShaderProgram&&) = default;
|
||||
virtual ~OpenGLShaderProgram();
|
||||
|
||||
GLuint GetAttributeLocation(const char* name);
|
||||
GLuint GetUniformLocation(const char* name);
|
||||
void Use();
|
||||
GLuint GetAttributeLocation(const char* name);
|
||||
GLuint GetUniformLocation(const char* name);
|
||||
void Use();
|
||||
|
||||
private:
|
||||
bool Link();
|
||||
};
|
||||
private:
|
||||
bool Link();
|
||||
};
|
||||
} // namespace OpenRCT2::Ui
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
# include "OpenGLFramebuffer.h"
|
||||
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
constexpr GLfloat depthValue[1] = { 1.0f };
|
||||
constexpr GLfloat depthValueTransparent[1] = { 0.0f };
|
||||
constexpr GLuint indexValue[4] = { 0, 0, 0, 0 };
|
||||
|
|
|
@ -14,44 +14,46 @@
|
|||
#include "OpenGLFramebuffer.h"
|
||||
|
||||
#include <openrct2/common.h>
|
||||
|
||||
/**
|
||||
* Class to maintain two different framebuffers where the active framebuffer
|
||||
* will swap between the two, copying the other's pixels in the process for
|
||||
* performing pre-processing filters.
|
||||
*
|
||||
* When you need to bind the current frame to a shader, call SwapCopy and
|
||||
* then bind the value of GetSourceTexture to your shader.
|
||||
*/
|
||||
class SwapFramebuffer final
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
private:
|
||||
OpenGLFramebuffer _opaqueFramebuffer;
|
||||
OpenGLFramebuffer _transparentFramebuffer;
|
||||
OpenGLFramebuffer _mixFramebuffer;
|
||||
GLuint _backDepth;
|
||||
/**
|
||||
* Class to maintain two different framebuffers where the active framebuffer
|
||||
* will swap between the two, copying the other's pixels in the process for
|
||||
* performing pre-processing filters.
|
||||
*
|
||||
* When you need to bind the current frame to a shader, call SwapCopy and
|
||||
* then bind the value of GetSourceTexture to your shader.
|
||||
*/
|
||||
class SwapFramebuffer final
|
||||
{
|
||||
private:
|
||||
OpenGLFramebuffer _opaqueFramebuffer;
|
||||
OpenGLFramebuffer _transparentFramebuffer;
|
||||
OpenGLFramebuffer _mixFramebuffer;
|
||||
GLuint _backDepth;
|
||||
|
||||
public:
|
||||
SwapFramebuffer(int32_t width, int32_t height);
|
||||
~SwapFramebuffer();
|
||||
public:
|
||||
SwapFramebuffer(int32_t width, int32_t height);
|
||||
~SwapFramebuffer();
|
||||
|
||||
const OpenGLFramebuffer& GetFinalFramebuffer() const
|
||||
{
|
||||
return _opaqueFramebuffer;
|
||||
}
|
||||
GLuint GetBackDepthTexture() const
|
||||
{
|
||||
return _backDepth;
|
||||
}
|
||||
void BindOpaque()
|
||||
{
|
||||
_opaqueFramebuffer.Bind();
|
||||
}
|
||||
void BindTransparent()
|
||||
{
|
||||
_transparentFramebuffer.Bind();
|
||||
}
|
||||
const OpenGLFramebuffer& GetFinalFramebuffer() const
|
||||
{
|
||||
return _opaqueFramebuffer;
|
||||
}
|
||||
GLuint GetBackDepthTexture() const
|
||||
{
|
||||
return _backDepth;
|
||||
}
|
||||
void BindOpaque()
|
||||
{
|
||||
_opaqueFramebuffer.Bind();
|
||||
}
|
||||
void BindTransparent()
|
||||
{
|
||||
_transparentFramebuffer.Bind();
|
||||
}
|
||||
|
||||
void ApplyTransparency(ApplyTransparencyShader& shader, GLuint paletteTex, GLuint blendPaletteTex);
|
||||
void Clear();
|
||||
};
|
||||
void ApplyTransparency(ApplyTransparencyShader& shader, GLuint paletteTex, GLuint blendPaletteTex);
|
||||
void Clear();
|
||||
};
|
||||
} // namespace OpenRCT2::Ui
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
# include <stdexcept>
|
||||
# include <vector>
|
||||
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
constexpr uint32_t UNUSED_INDEX = 0xFFFFFFFF;
|
||||
|
||||
TextureCache::TextureCache()
|
||||
|
|
|
@ -29,220 +29,223 @@ struct DrawPixelInfo;
|
|||
struct PaletteMap;
|
||||
enum class FilterPaletteID : int32_t;
|
||||
|
||||
struct GlyphId
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
ImageIndex Image;
|
||||
uint64_t Palette;
|
||||
|
||||
struct Hash
|
||||
struct GlyphId
|
||||
{
|
||||
size_t operator()(const GlyphId& k) const
|
||||
ImageIndex Image;
|
||||
uint64_t Palette;
|
||||
|
||||
struct Hash
|
||||
{
|
||||
size_t hash = k.Image * 7;
|
||||
hash += (k.Palette & 0xFFFFFFFFUL) * 13;
|
||||
hash += (k.Palette >> 32uL) * 23;
|
||||
return hash;
|
||||
size_t operator()(const GlyphId& k) const
|
||||
{
|
||||
size_t hash = k.Image * 7;
|
||||
hash += (k.Palette & 0xFFFFFFFFUL) * 13;
|
||||
hash += (k.Palette >> 32uL) * 23;
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
struct Equal
|
||||
{
|
||||
bool operator()(const GlyphId& lhs, const GlyphId& rhs) const
|
||||
{
|
||||
return lhs.Image == rhs.Image && lhs.Palette == rhs.Palette;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// This is the maximum width and height of each atlas, basically the
|
||||
// granularity at which new atlases are allocated (2048 -> 4 MB of VRAM)
|
||||
constexpr int32_t TEXTURE_CACHE_MAX_ATLAS_SIZE = 2048;
|
||||
|
||||
// Pixel dimensions of smallest supported slots in texture atlases
|
||||
// Must be a power of 2!
|
||||
constexpr int32_t TEXTURE_CACHE_SMALLEST_SLOT = 32;
|
||||
|
||||
struct BasicTextureInfo
|
||||
{
|
||||
GLuint index;
|
||||
vec4 normalizedBounds;
|
||||
};
|
||||
|
||||
// Location of an image (texture atlas index, slot and normalized coordinates)
|
||||
struct AtlasTextureInfo : public BasicTextureInfo
|
||||
{
|
||||
GLuint slot;
|
||||
ivec4 bounds;
|
||||
ImageIndex image;
|
||||
};
|
||||
|
||||
// Represents a texture atlas that images of a given maximum size can be allocated from
|
||||
// Atlases are all stored in the same 2D texture array, occupying the specified index
|
||||
// Slots in atlases are always squares.
|
||||
class Atlas final
|
||||
{
|
||||
private:
|
||||
GLuint _index = 0;
|
||||
int32_t _imageSize = 0;
|
||||
int32_t _atlasWidth = 0;
|
||||
int32_t _atlasHeight = 0;
|
||||
std::vector<GLuint> _freeSlots;
|
||||
|
||||
int32_t _cols = 0;
|
||||
int32_t _rows = 0;
|
||||
|
||||
public:
|
||||
Atlas(GLuint index, int32_t imageSize)
|
||||
: _index(index)
|
||||
, _imageSize(imageSize)
|
||||
{
|
||||
}
|
||||
|
||||
void Initialise(int32_t atlasWidth, int32_t atlasHeight)
|
||||
{
|
||||
_atlasWidth = atlasWidth;
|
||||
_atlasHeight = atlasHeight;
|
||||
|
||||
_cols = std::max(1, _atlasWidth / _imageSize);
|
||||
_rows = std::max(1, _atlasHeight / _imageSize);
|
||||
|
||||
_freeSlots.resize(_cols * _rows);
|
||||
for (size_t i = 0; i < _freeSlots.size(); i++)
|
||||
{
|
||||
_freeSlots[i] = static_cast<GLuint>(i);
|
||||
}
|
||||
}
|
||||
|
||||
AtlasTextureInfo Allocate(int32_t actualWidth, int32_t actualHeight)
|
||||
{
|
||||
assert(!_freeSlots.empty());
|
||||
|
||||
GLuint slot = _freeSlots.back();
|
||||
_freeSlots.pop_back();
|
||||
|
||||
auto bounds = GetSlotCoordinates(slot, actualWidth, actualHeight);
|
||||
|
||||
AtlasTextureInfo info{};
|
||||
info.index = _index;
|
||||
info.slot = slot;
|
||||
info.bounds = bounds;
|
||||
info.normalizedBounds = NormalizeCoordinates(bounds);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
void Free(const AtlasTextureInfo& info)
|
||||
{
|
||||
assert(_index == info.index);
|
||||
|
||||
_freeSlots.push_back(info.slot);
|
||||
}
|
||||
|
||||
// Checks if specified image would be tightly packed in this atlas
|
||||
// by checking if it is within the right power of 2 range
|
||||
[[nodiscard]] bool IsImageSuitable(int32_t actualWidth, int32_t actualHeight) const
|
||||
{
|
||||
int32_t imageOrder = CalculateImageSizeOrder(actualWidth, actualHeight);
|
||||
int32_t atlasOrder = log2(_imageSize);
|
||||
|
||||
return imageOrder == atlasOrder;
|
||||
}
|
||||
|
||||
[[nodiscard]] int32_t GetFreeSlots() const
|
||||
{
|
||||
return static_cast<int32_t>(_freeSlots.size());
|
||||
}
|
||||
|
||||
static int32_t CalculateImageSizeOrder(int32_t actualWidth, int32_t actualHeight)
|
||||
{
|
||||
int32_t actualSize = std::max(actualWidth, actualHeight);
|
||||
|
||||
if (actualSize < TEXTURE_CACHE_SMALLEST_SLOT)
|
||||
{
|
||||
actualSize = TEXTURE_CACHE_SMALLEST_SLOT;
|
||||
}
|
||||
|
||||
return static_cast<int32_t>(ceil(log2f(static_cast<float>(actualSize))));
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] ivec4 GetSlotCoordinates(GLuint slot, int32_t actualWidth, int32_t actualHeight) const
|
||||
{
|
||||
int32_t row = slot / _cols;
|
||||
int32_t col = slot % _cols;
|
||||
|
||||
return ivec4{
|
||||
_imageSize * col,
|
||||
_imageSize * row,
|
||||
_imageSize * col + actualWidth,
|
||||
_imageSize * row + actualHeight,
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] vec4 NormalizeCoordinates(const ivec4& coords) const
|
||||
{
|
||||
return vec4{
|
||||
coords.x / static_cast<float>(_atlasWidth),
|
||||
coords.y / static_cast<float>(_atlasHeight),
|
||||
coords.z / static_cast<float>(_atlasWidth),
|
||||
coords.w / static_cast<float>(_atlasHeight),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
struct Equal
|
||||
class TextureCache final
|
||||
{
|
||||
bool operator()(const GlyphId& lhs, const GlyphId& rhs) const
|
||||
{
|
||||
return lhs.Image == rhs.Image && lhs.Palette == rhs.Palette;
|
||||
}
|
||||
};
|
||||
};
|
||||
private:
|
||||
bool _initialized = false;
|
||||
|
||||
// This is the maximum width and height of each atlas, basically the
|
||||
// granularity at which new atlases are allocated (2048 -> 4 MB of VRAM)
|
||||
constexpr int32_t TEXTURE_CACHE_MAX_ATLAS_SIZE = 2048;
|
||||
GLuint _atlasesTexture = 0;
|
||||
GLint _atlasesTextureDimensions = 0;
|
||||
GLuint _atlasesTextureCapacity = 0;
|
||||
GLuint _atlasesTextureIndices = 0;
|
||||
GLint _atlasesTextureIndicesLimit = 0;
|
||||
std::vector<Atlas> _atlases;
|
||||
std::unordered_map<GlyphId, AtlasTextureInfo, GlyphId::Hash, GlyphId::Equal> _glyphTextureMap;
|
||||
std::vector<AtlasTextureInfo> _textureCache;
|
||||
std::array<uint32_t, SPR_IMAGE_LIST_END> _indexMap;
|
||||
|
||||
// Pixel dimensions of smallest supported slots in texture atlases
|
||||
// Must be a power of 2!
|
||||
constexpr int32_t TEXTURE_CACHE_SMALLEST_SLOT = 32;
|
||||
|
||||
struct BasicTextureInfo
|
||||
{
|
||||
GLuint index;
|
||||
vec4 normalizedBounds;
|
||||
};
|
||||
|
||||
// Location of an image (texture atlas index, slot and normalized coordinates)
|
||||
struct AtlasTextureInfo : public BasicTextureInfo
|
||||
{
|
||||
GLuint slot;
|
||||
ivec4 bounds;
|
||||
ImageIndex image;
|
||||
};
|
||||
|
||||
// Represents a texture atlas that images of a given maximum size can be allocated from
|
||||
// Atlases are all stored in the same 2D texture array, occupying the specified index
|
||||
// Slots in atlases are always squares.
|
||||
class Atlas final
|
||||
{
|
||||
private:
|
||||
GLuint _index = 0;
|
||||
int32_t _imageSize = 0;
|
||||
int32_t _atlasWidth = 0;
|
||||
int32_t _atlasHeight = 0;
|
||||
std::vector<GLuint> _freeSlots;
|
||||
|
||||
int32_t _cols = 0;
|
||||
int32_t _rows = 0;
|
||||
|
||||
public:
|
||||
Atlas(GLuint index, int32_t imageSize)
|
||||
: _index(index)
|
||||
, _imageSize(imageSize)
|
||||
{
|
||||
}
|
||||
|
||||
void Initialise(int32_t atlasWidth, int32_t atlasHeight)
|
||||
{
|
||||
_atlasWidth = atlasWidth;
|
||||
_atlasHeight = atlasHeight;
|
||||
|
||||
_cols = std::max(1, _atlasWidth / _imageSize);
|
||||
_rows = std::max(1, _atlasHeight / _imageSize);
|
||||
|
||||
_freeSlots.resize(_cols * _rows);
|
||||
for (size_t i = 0; i < _freeSlots.size(); i++)
|
||||
{
|
||||
_freeSlots[i] = static_cast<GLuint>(i);
|
||||
}
|
||||
}
|
||||
|
||||
AtlasTextureInfo Allocate(int32_t actualWidth, int32_t actualHeight)
|
||||
{
|
||||
assert(!_freeSlots.empty());
|
||||
|
||||
GLuint slot = _freeSlots.back();
|
||||
_freeSlots.pop_back();
|
||||
|
||||
auto bounds = GetSlotCoordinates(slot, actualWidth, actualHeight);
|
||||
|
||||
AtlasTextureInfo info{};
|
||||
info.index = _index;
|
||||
info.slot = slot;
|
||||
info.bounds = bounds;
|
||||
info.normalizedBounds = NormalizeCoordinates(bounds);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
void Free(const AtlasTextureInfo& info)
|
||||
{
|
||||
assert(_index == info.index);
|
||||
|
||||
_freeSlots.push_back(info.slot);
|
||||
}
|
||||
|
||||
// Checks if specified image would be tightly packed in this atlas
|
||||
// by checking if it is within the right power of 2 range
|
||||
[[nodiscard]] bool IsImageSuitable(int32_t actualWidth, int32_t actualHeight) const
|
||||
{
|
||||
int32_t imageOrder = CalculateImageSizeOrder(actualWidth, actualHeight);
|
||||
int32_t atlasOrder = log2(_imageSize);
|
||||
|
||||
return imageOrder == atlasOrder;
|
||||
}
|
||||
|
||||
[[nodiscard]] int32_t GetFreeSlots() const
|
||||
{
|
||||
return static_cast<int32_t>(_freeSlots.size());
|
||||
}
|
||||
|
||||
static int32_t CalculateImageSizeOrder(int32_t actualWidth, int32_t actualHeight)
|
||||
{
|
||||
int32_t actualSize = std::max(actualWidth, actualHeight);
|
||||
|
||||
if (actualSize < TEXTURE_CACHE_SMALLEST_SLOT)
|
||||
{
|
||||
actualSize = TEXTURE_CACHE_SMALLEST_SLOT;
|
||||
}
|
||||
|
||||
return static_cast<int32_t>(ceil(log2f(static_cast<float>(actualSize))));
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] ivec4 GetSlotCoordinates(GLuint slot, int32_t actualWidth, int32_t actualHeight) const
|
||||
{
|
||||
int32_t row = slot / _cols;
|
||||
int32_t col = slot % _cols;
|
||||
|
||||
return ivec4{
|
||||
_imageSize * col,
|
||||
_imageSize * row,
|
||||
_imageSize * col + actualWidth,
|
||||
_imageSize * row + actualHeight,
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] vec4 NormalizeCoordinates(const ivec4& coords) const
|
||||
{
|
||||
return vec4{
|
||||
coords.x / static_cast<float>(_atlasWidth),
|
||||
coords.y / static_cast<float>(_atlasHeight),
|
||||
coords.z / static_cast<float>(_atlasWidth),
|
||||
coords.w / static_cast<float>(_atlasHeight),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class TextureCache final
|
||||
{
|
||||
private:
|
||||
bool _initialized = false;
|
||||
|
||||
GLuint _atlasesTexture = 0;
|
||||
GLint _atlasesTextureDimensions = 0;
|
||||
GLuint _atlasesTextureCapacity = 0;
|
||||
GLuint _atlasesTextureIndices = 0;
|
||||
GLint _atlasesTextureIndicesLimit = 0;
|
||||
std::vector<Atlas> _atlases;
|
||||
std::unordered_map<GlyphId, AtlasTextureInfo, GlyphId::Hash, GlyphId::Equal> _glyphTextureMap;
|
||||
std::vector<AtlasTextureInfo> _textureCache;
|
||||
std::array<uint32_t, SPR_IMAGE_LIST_END> _indexMap;
|
||||
|
||||
GLuint _paletteTexture = 0;
|
||||
GLuint _blendPaletteTexture = 0;
|
||||
GLuint _paletteTexture = 0;
|
||||
GLuint _blendPaletteTexture = 0;
|
||||
|
||||
#ifndef __MACOSX__
|
||||
std::shared_mutex _mutex;
|
||||
using shared_lock = std::shared_lock<std::shared_mutex>;
|
||||
using unique_lock = std::unique_lock<std::shared_mutex>;
|
||||
std::shared_mutex _mutex;
|
||||
using shared_lock = std::shared_lock<std::shared_mutex>;
|
||||
using unique_lock = std::unique_lock<std::shared_mutex>;
|
||||
#else
|
||||
std::mutex _mutex;
|
||||
using shared_lock = std::unique_lock<std::mutex>;
|
||||
using unique_lock = std::unique_lock<std::mutex>;
|
||||
std::mutex _mutex;
|
||||
using shared_lock = std::unique_lock<std::mutex>;
|
||||
using unique_lock = std::unique_lock<std::mutex>;
|
||||
#endif
|
||||
|
||||
public:
|
||||
TextureCache();
|
||||
~TextureCache();
|
||||
void InvalidateImage(ImageIndex image);
|
||||
BasicTextureInfo GetOrLoadImageTexture(const ImageId imageId);
|
||||
BasicTextureInfo GetOrLoadGlyphTexture(const ImageId imageId, const PaletteMap& paletteMap);
|
||||
BasicTextureInfo GetOrLoadBitmapTexture(ImageIndex image, const void* pixels, size_t width, size_t height);
|
||||
public:
|
||||
TextureCache();
|
||||
~TextureCache();
|
||||
void InvalidateImage(ImageIndex image);
|
||||
BasicTextureInfo GetOrLoadImageTexture(const ImageId imageId);
|
||||
BasicTextureInfo GetOrLoadGlyphTexture(const ImageId imageId, const PaletteMap& paletteMap);
|
||||
BasicTextureInfo GetOrLoadBitmapTexture(ImageIndex image, const void* pixels, size_t width, size_t height);
|
||||
|
||||
GLuint GetAtlasesTexture();
|
||||
GLuint GetPaletteTexture();
|
||||
GLuint GetBlendPaletteTexture();
|
||||
static GLint PaletteToY(FilterPaletteID palette);
|
||||
GLuint GetAtlasesTexture();
|
||||
GLuint GetPaletteTexture();
|
||||
GLuint GetBlendPaletteTexture();
|
||||
static GLint PaletteToY(FilterPaletteID palette);
|
||||
|
||||
private:
|
||||
void CreateTextures();
|
||||
void GeneratePaletteTexture();
|
||||
void EnlargeAtlasesTexture(GLuint newEntries);
|
||||
AtlasTextureInfo LoadImageTexture(const ImageId image);
|
||||
AtlasTextureInfo LoadGlyphTexture(const ImageId image, const PaletteMap& paletteMap);
|
||||
AtlasTextureInfo AllocateImage(int32_t imageWidth, int32_t imageHeight);
|
||||
AtlasTextureInfo LoadBitmapTexture(ImageIndex image, const void* pixels, size_t width, size_t height);
|
||||
static DrawPixelInfo GetImageAsDPI(const ImageId imageId);
|
||||
static DrawPixelInfo GetGlyphAsDPI(const ImageId imageId, const PaletteMap& paletteMap);
|
||||
void FreeTextures();
|
||||
private:
|
||||
void CreateTextures();
|
||||
void GeneratePaletteTexture();
|
||||
void EnlargeAtlasesTexture(GLuint newEntries);
|
||||
AtlasTextureInfo LoadImageTexture(const ImageId image);
|
||||
AtlasTextureInfo LoadGlyphTexture(const ImageId image, const PaletteMap& paletteMap);
|
||||
AtlasTextureInfo AllocateImage(int32_t imageWidth, int32_t imageHeight);
|
||||
AtlasTextureInfo LoadBitmapTexture(ImageIndex image, const void* pixels, size_t width, size_t height);
|
||||
static DrawPixelInfo GetImageAsDPI(const ImageId imageId);
|
||||
static DrawPixelInfo GetGlyphAsDPI(const ImageId imageId, const PaletteMap& paletteMap);
|
||||
void FreeTextures();
|
||||
|
||||
static DrawPixelInfo CreateDPI(int32_t width, int32_t height);
|
||||
static void DeleteDPI(DrawPixelInfo dpi);
|
||||
};
|
||||
static DrawPixelInfo CreateDPI(int32_t width, int32_t height);
|
||||
static void DeleteDPI(DrawPixelInfo dpi);
|
||||
};
|
||||
} // namespace OpenRCT2::Ui
|
||||
|
|
|
@ -15,194 +15,196 @@
|
|||
# include <map>
|
||||
# include <vector>
|
||||
|
||||
/*
|
||||
* Structure to store locations of vertical bounding box edge.
|
||||
*/
|
||||
struct XData
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
int32_t xposition;
|
||||
bool begin;
|
||||
int32_t top, bottom;
|
||||
};
|
||||
using SweepLine = std::vector<XData>;
|
||||
|
||||
/*
|
||||
* Creates a list of vertical bounding box edges, stored as xdata and sorted
|
||||
* from left to right. If multiple edges are at the same x coordinate, Then
|
||||
* edges for boxes to the left will appear before edges for boxes to the right.
|
||||
*/
|
||||
static inline SweepLine CreateXList(const RectCommandBatch& transparent)
|
||||
{
|
||||
SweepLine x_sweep;
|
||||
x_sweep.reserve(transparent.size() * 2);
|
||||
|
||||
for (const DrawRectCommand& command : transparent)
|
||||
/*
|
||||
* Structure to store locations of vertical bounding box edge.
|
||||
*/
|
||||
struct XData
|
||||
{
|
||||
int32_t left = std::min(std::max(command.bounds.x, command.clip.x), command.clip.z);
|
||||
int32_t top = std::min(std::max(command.bounds.y, command.clip.y), command.clip.w);
|
||||
int32_t right = std::min(std::max(command.bounds.z, command.clip.x), command.clip.z);
|
||||
int32_t bottom = std::min(std::max(command.bounds.w, command.clip.y), command.clip.w);
|
||||
int32_t xposition;
|
||||
bool begin;
|
||||
int32_t top, bottom;
|
||||
};
|
||||
using SweepLine = std::vector<XData>;
|
||||
|
||||
assert(left <= right);
|
||||
assert(top <= bottom);
|
||||
if (left == right)
|
||||
continue;
|
||||
if (top == bottom)
|
||||
continue;
|
||||
|
||||
x_sweep.push_back({ left, true, top, bottom });
|
||||
x_sweep.push_back({ right, false, top, bottom });
|
||||
}
|
||||
|
||||
std::sort(x_sweep.begin(), x_sweep.end(), [](const XData& a, const XData& b) -> bool {
|
||||
if (a.xposition != b.xposition)
|
||||
return a.xposition < b.xposition;
|
||||
|
||||
return !a.begin && b.begin;
|
||||
});
|
||||
|
||||
return x_sweep;
|
||||
}
|
||||
|
||||
/*
|
||||
* Structure that stores intervals. YData.count stores how many intervals have
|
||||
* an endpoint at this position, and YData.depth stores how many intervals
|
||||
* intersect on the left limit of this point. In other words, IntervalTree
|
||||
* stores half-closed intervals, with the left endpoint open, and the right
|
||||
* endpoint closed. The IntervalTree uses std::map because it stores the values
|
||||
* sorted, and we can traverse it in order using its bidirectional iterators.
|
||||
*/
|
||||
struct YData
|
||||
{
|
||||
int32_t count, depth;
|
||||
};
|
||||
using IntervalTree = std::map<int32_t, YData>;
|
||||
|
||||
/*
|
||||
* Inserts the interval's top endpoint into the interval tree. If the endpoint
|
||||
* already exists in the interval tree, it stacks the endpoints.
|
||||
*/
|
||||
static inline IntervalTree::iterator InsertTopEndpoint(IntervalTree& y_intersect, int32_t top)
|
||||
{
|
||||
auto top_in = y_intersect.insert({ top, { 1, 0 } });
|
||||
IntervalTree::iterator top_it = top_in.first;
|
||||
if (top_in.second)
|
||||
/*
|
||||
* Creates a list of vertical bounding box edges, stored as xdata and sorted
|
||||
* from left to right. If multiple edges are at the same x coordinate, Then
|
||||
* edges for boxes to the left will appear before edges for boxes to the right.
|
||||
*/
|
||||
static inline SweepLine CreateXList(const RectCommandBatch& transparent)
|
||||
{
|
||||
IntervalTree::iterator top_next = std::next(top_it);
|
||||
if (top_next != y_intersect.end())
|
||||
SweepLine x_sweep;
|
||||
x_sweep.reserve(transparent.size() * 2);
|
||||
|
||||
for (const DrawRectCommand& command : transparent)
|
||||
{
|
||||
top_it->second.depth = top_next->second.depth;
|
||||
int32_t left = std::min(std::max(command.bounds.x, command.clip.x), command.clip.z);
|
||||
int32_t top = std::min(std::max(command.bounds.y, command.clip.y), command.clip.w);
|
||||
int32_t right = std::min(std::max(command.bounds.z, command.clip.x), command.clip.z);
|
||||
int32_t bottom = std::min(std::max(command.bounds.w, command.clip.y), command.clip.w);
|
||||
|
||||
assert(left <= right);
|
||||
assert(top <= bottom);
|
||||
if (left == right)
|
||||
continue;
|
||||
if (top == bottom)
|
||||
continue;
|
||||
|
||||
x_sweep.push_back({ left, true, top, bottom });
|
||||
x_sweep.push_back({ right, false, top, bottom });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++top_it->second.count;
|
||||
}
|
||||
return top_it;
|
||||
}
|
||||
|
||||
/*
|
||||
* Inserts the interval's bottom endpoint into the interval tree. If the
|
||||
* endpoint already exists in the interval tree, it stacks the endpoint.
|
||||
* This function can produce a new maximum depth.
|
||||
*/
|
||||
static inline IntervalTree::iterator InsertBottomEndpoint(IntervalTree& y_intersect, int32_t bottom)
|
||||
{
|
||||
auto bottom_in = y_intersect.insert({ bottom, { 1, 1 } });
|
||||
IntervalTree::iterator bottom_it = bottom_in.first;
|
||||
if (bottom_in.second)
|
||||
std::sort(x_sweep.begin(), x_sweep.end(), [](const XData& a, const XData& b) -> bool {
|
||||
if (a.xposition != b.xposition)
|
||||
return a.xposition < b.xposition;
|
||||
|
||||
return !a.begin && b.begin;
|
||||
});
|
||||
|
||||
return x_sweep;
|
||||
}
|
||||
|
||||
/*
|
||||
* Structure that stores intervals. YData.count stores how many intervals have
|
||||
* an endpoint at this position, and YData.depth stores how many intervals
|
||||
* intersect on the left limit of this point. In other words, IntervalTree
|
||||
* stores half-closed intervals, with the left endpoint open, and the right
|
||||
* endpoint closed. The IntervalTree uses std::map because it stores the values
|
||||
* sorted, and we can traverse it in order using its bidirectional iterators.
|
||||
*/
|
||||
struct YData
|
||||
{
|
||||
IntervalTree::iterator bottom_next = std::next(bottom_it);
|
||||
if (bottom_next != y_intersect.end())
|
||||
int32_t count, depth;
|
||||
};
|
||||
using IntervalTree = std::map<int32_t, YData>;
|
||||
|
||||
/*
|
||||
* Inserts the interval's top endpoint into the interval tree. If the endpoint
|
||||
* already exists in the interval tree, it stacks the endpoints.
|
||||
*/
|
||||
static inline IntervalTree::iterator InsertTopEndpoint(IntervalTree& y_intersect, int32_t top)
|
||||
{
|
||||
auto top_in = y_intersect.insert({ top, { 1, 0 } });
|
||||
IntervalTree::iterator top_it = top_in.first;
|
||||
if (top_in.second)
|
||||
{
|
||||
bottom_it->second.depth = bottom_next->second.depth + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++bottom_it->second.count;
|
||||
++bottom_it->second.depth;
|
||||
}
|
||||
return bottom_it;
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes the interval's top endpoint, handling stacked endpoints.
|
||||
*/
|
||||
static inline void RemoveTopEndpoint(IntervalTree& y_intersect, IntervalTree::iterator top_it)
|
||||
{
|
||||
if (top_it->second.count == 1)
|
||||
{
|
||||
y_intersect.erase(top_it);
|
||||
}
|
||||
else
|
||||
{
|
||||
--top_it->second.count;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes the interval's bottom endpoint, handling stacked endpoints.
|
||||
*/
|
||||
static inline void RemoveBottomEndpoint(IntervalTree& y_intersect, IntervalTree::iterator bottom_it)
|
||||
{
|
||||
if (bottom_it->second.count == 1)
|
||||
{
|
||||
y_intersect.erase(bottom_it);
|
||||
}
|
||||
else
|
||||
{
|
||||
--bottom_it->second.count;
|
||||
--bottom_it->second.depth;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines an approximation of the number of depth peeling iterations needed
|
||||
* to render the command batch. It will never underestimate the number of
|
||||
* iterations, but it can overestimate, usually by no more than +2.
|
||||
*/
|
||||
int32_t MaxTransparencyDepth(const RectCommandBatch& transparent)
|
||||
{
|
||||
int32_t max_depth = 1;
|
||||
SweepLine x_sweep = CreateXList(transparent);
|
||||
IntervalTree y_intersect{};
|
||||
|
||||
for (const XData& x : x_sweep)
|
||||
{
|
||||
if (x.begin)
|
||||
{
|
||||
IntervalTree::iterator top_it = InsertTopEndpoint(y_intersect, x.top);
|
||||
IntervalTree::iterator bottom_it = InsertBottomEndpoint(y_intersect, x.bottom);
|
||||
max_depth = std::max(max_depth, bottom_it->second.depth);
|
||||
|
||||
/*
|
||||
* Increment the depth for endpoings that intersect this interval
|
||||
*/
|
||||
for (IntervalTree::iterator it = std::next(top_it); it != bottom_it && it != std::end(y_intersect); ++it)
|
||||
IntervalTree::iterator top_next = std::next(top_it);
|
||||
if (top_next != y_intersect.end())
|
||||
{
|
||||
max_depth = std::max(max_depth, ++it->second.depth);
|
||||
top_it->second.depth = top_next->second.depth;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IntervalTree::iterator top_it = y_intersect.find(x.top);
|
||||
IntervalTree::iterator bottom_it = y_intersect.find(x.bottom);
|
||||
++top_it->second.count;
|
||||
}
|
||||
return top_it;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decrement the depth for endpoings that intersected this interval
|
||||
*/
|
||||
for (IntervalTree::iterator it = std::next(top_it); it != bottom_it && it != std::end(y_intersect); ++it)
|
||||
/*
|
||||
* Inserts the interval's bottom endpoint into the interval tree. If the
|
||||
* endpoint already exists in the interval tree, it stacks the endpoint.
|
||||
* This function can produce a new maximum depth.
|
||||
*/
|
||||
static inline IntervalTree::iterator InsertBottomEndpoint(IntervalTree& y_intersect, int32_t bottom)
|
||||
{
|
||||
auto bottom_in = y_intersect.insert({ bottom, { 1, 1 } });
|
||||
IntervalTree::iterator bottom_it = bottom_in.first;
|
||||
if (bottom_in.second)
|
||||
{
|
||||
IntervalTree::iterator bottom_next = std::next(bottom_it);
|
||||
if (bottom_next != y_intersect.end())
|
||||
{
|
||||
--it->second.depth;
|
||||
bottom_it->second.depth = bottom_next->second.depth + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
++bottom_it->second.count;
|
||||
++bottom_it->second.depth;
|
||||
}
|
||||
return bottom_it;
|
||||
}
|
||||
|
||||
RemoveTopEndpoint(y_intersect, top_it);
|
||||
RemoveBottomEndpoint(y_intersect, bottom_it);
|
||||
/*
|
||||
* Removes the interval's top endpoint, handling stacked endpoints.
|
||||
*/
|
||||
static inline void RemoveTopEndpoint(IntervalTree& y_intersect, IntervalTree::iterator top_it)
|
||||
{
|
||||
if (top_it->second.count == 1)
|
||||
{
|
||||
y_intersect.erase(top_it);
|
||||
}
|
||||
else
|
||||
{
|
||||
--top_it->second.count;
|
||||
}
|
||||
}
|
||||
|
||||
return max_depth;
|
||||
}
|
||||
/*
|
||||
* Removes the interval's bottom endpoint, handling stacked endpoints.
|
||||
*/
|
||||
static inline void RemoveBottomEndpoint(IntervalTree& y_intersect, IntervalTree::iterator bottom_it)
|
||||
{
|
||||
if (bottom_it->second.count == 1)
|
||||
{
|
||||
y_intersect.erase(bottom_it);
|
||||
}
|
||||
else
|
||||
{
|
||||
--bottom_it->second.count;
|
||||
--bottom_it->second.depth;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines an approximation of the number of depth peeling iterations needed
|
||||
* to render the command batch. It will never underestimate the number of
|
||||
* iterations, but it can overestimate, usually by no more than +2.
|
||||
*/
|
||||
int32_t MaxTransparencyDepth(const RectCommandBatch& transparent)
|
||||
{
|
||||
int32_t max_depth = 1;
|
||||
SweepLine x_sweep = CreateXList(transparent);
|
||||
IntervalTree y_intersect{};
|
||||
|
||||
for (const XData& x : x_sweep)
|
||||
{
|
||||
if (x.begin)
|
||||
{
|
||||
IntervalTree::iterator top_it = InsertTopEndpoint(y_intersect, x.top);
|
||||
IntervalTree::iterator bottom_it = InsertBottomEndpoint(y_intersect, x.bottom);
|
||||
max_depth = std::max(max_depth, bottom_it->second.depth);
|
||||
|
||||
/*
|
||||
* Increment the depth for endpoings that intersect this interval
|
||||
*/
|
||||
for (IntervalTree::iterator it = std::next(top_it); it != bottom_it && it != std::end(y_intersect); ++it)
|
||||
{
|
||||
max_depth = std::max(max_depth, ++it->second.depth);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IntervalTree::iterator top_it = y_intersect.find(x.top);
|
||||
IntervalTree::iterator bottom_it = y_intersect.find(x.bottom);
|
||||
|
||||
/*
|
||||
* Decrement the depth for endpoings that intersected this interval
|
||||
*/
|
||||
for (IntervalTree::iterator it = std::next(top_it); it != bottom_it && it != std::end(y_intersect); ++it)
|
||||
{
|
||||
--it->second.depth;
|
||||
}
|
||||
|
||||
RemoveTopEndpoint(y_intersect, top_it);
|
||||
RemoveBottomEndpoint(y_intersect, bottom_it);
|
||||
}
|
||||
}
|
||||
|
||||
return max_depth;
|
||||
}
|
||||
} // namespace OpenRCT2::Ui
|
||||
#endif /* DISABLE_OPENGL */
|
||||
|
|
|
@ -13,9 +13,12 @@
|
|||
|
||||
#include <openrct2/common.h>
|
||||
|
||||
/*
|
||||
* Determines an approximation of the number of depth peeling iterations needed
|
||||
* to render the command batch. It will never underestimate the number of
|
||||
* iterations, but it can overestimate, usually by no more than +2.
|
||||
*/
|
||||
int32_t MaxTransparencyDepth(const RectCommandBatch& transparent);
|
||||
namespace OpenRCT2::Ui
|
||||
{
|
||||
/*
|
||||
* Determines an approximation of the number of depth peeling iterations needed
|
||||
* to render the command batch. It will never underestimate the number of
|
||||
* iterations, but it can overestimate, usually by no more than +2.
|
||||
*/
|
||||
int32_t MaxTransparencyDepth(const RectCommandBatch& transparent);
|
||||
} // namespace OpenRCT2::Ui
|
||||
|
|
|
@ -202,7 +202,7 @@ void InputManager::Process(const InputEvent& e)
|
|||
{
|
||||
if (e.State == InputEventState::Release)
|
||||
{
|
||||
WindowTextInputKey(w, e.Button);
|
||||
OpenRCT2::Ui::Windows::WindowTextInputKey(w, e.Button);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@
|
|||
#include <openrct2/world/Scenery.h>
|
||||
#include <optional>
|
||||
|
||||
using namespace OpenRCT2::Ui::Windows;
|
||||
|
||||
struct RCTMouseData
|
||||
{
|
||||
uint32_t x;
|
||||
|
@ -390,7 +392,7 @@ static void GameHandleInputMouse(const ScreenCoordsXY& screenCoords, MouseState
|
|||
break;
|
||||
}
|
||||
|
||||
WindowEventToolDragCall(w, gCurrentToolWidget.widget_index, screenCoords);
|
||||
w->OnToolDrag(gCurrentToolWidget.widget_index, screenCoords);
|
||||
break;
|
||||
case MouseState::LeftRelease:
|
||||
_inputState = InputState::Reset;
|
||||
|
@ -401,7 +403,7 @@ static void GameHandleInputMouse(const ScreenCoordsXY& screenCoords, MouseState
|
|||
w = WindowFindByNumber(gCurrentToolWidget.window_classification, gCurrentToolWidget.window_number);
|
||||
if (w != nullptr)
|
||||
{
|
||||
WindowEventToolUpCall(w, gCurrentToolWidget.widget_index, screenCoords);
|
||||
w->OnToolUp(gCurrentToolWidget.widget_index, screenCoords);
|
||||
}
|
||||
}
|
||||
else if (!(_inputFlags & INPUT_FLAG_4))
|
||||
|
@ -482,7 +484,7 @@ static void InputWindowPositionEnd(WindowBase& w, const ScreenCoordsXY& screenCo
|
|||
_inputState = InputState::Normal;
|
||||
gTooltipCloseTimeout = 0;
|
||||
gTooltipWidget = _dragWidget;
|
||||
WindowEventMovedCall(&w, screenCoords);
|
||||
w.OnMoved(screenCoords);
|
||||
}
|
||||
|
||||
static void InputWindowResizeBegin(WindowBase& w, WidgetIndex widgetIndex, const ScreenCoordsXY& screenCoords)
|
||||
|
@ -625,10 +627,10 @@ static void InputScrollBegin(WindowBase& w, WidgetIndex widgetIndex, const Scree
|
|||
|
||||
_currentScrollArea = scroll_area;
|
||||
_currentScrollIndex = scroll_id;
|
||||
WindowEventScrollSelectCall(&w, scroll_id, scroll_area);
|
||||
w.OnScrollSelect(scroll_id, scroll_area);
|
||||
if (scroll_area == SCROLL_PART_VIEW)
|
||||
{
|
||||
WindowEventScrollMousedownCall(&w, scroll_id, scrollCoords);
|
||||
w.OnScrollMouseDown(scroll_id, scrollCoords);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -637,12 +639,12 @@ static void InputScrollBegin(WindowBase& w, WidgetIndex widgetIndex, const Scree
|
|||
|
||||
int32_t widget_width = widg.width() - 1;
|
||||
if (scroll.flags & VSCROLLBAR_VISIBLE)
|
||||
widget_width -= SCROLLBAR_WIDTH + 1;
|
||||
widget_width -= kScrollBarWidth + 1;
|
||||
int32_t widget_content_width = std::max(scroll.h_right - widget_width, 0);
|
||||
|
||||
int32_t widget_height = widg.bottom - widg.top - 1;
|
||||
if (scroll.flags & HSCROLLBAR_VISIBLE)
|
||||
widget_height -= SCROLLBAR_WIDTH + 1;
|
||||
widget_height -= kScrollBarWidth + 1;
|
||||
int32_t widget_content_height = std::max(scroll.v_bottom - widget_height, 0);
|
||||
|
||||
switch (scroll_area)
|
||||
|
@ -718,7 +720,7 @@ static void InputScrollContinue(WindowBase& w, WidgetIndex widgetIndex, const Sc
|
|||
switch (scroll_part)
|
||||
{
|
||||
case SCROLL_PART_VIEW:
|
||||
WindowEventScrollMousedragCall(&w, scroll_id, newScreenCoords);
|
||||
w.OnScrollMouseDrag(scroll_id, newScreenCoords);
|
||||
break;
|
||||
case SCROLL_PART_HSCROLLBAR_LEFT:
|
||||
InputScrollPartUpdateHLeft(w, widgetIndex, scroll_id);
|
||||
|
@ -757,7 +759,7 @@ static void InputScrollPartUpdateHThumb(WindowBase& w, WidgetIndex widgetIndex,
|
|||
newLeft *= x;
|
||||
x = widget.width() - 21;
|
||||
if (scroll.flags & VSCROLLBAR_VISIBLE)
|
||||
x -= SCROLLBAR_WIDTH + 1;
|
||||
x -= kScrollBarWidth + 1;
|
||||
newLeft /= x;
|
||||
x = newLeft;
|
||||
scroll.flags |= HSCROLLBAR_THUMB_PRESSED;
|
||||
|
@ -767,7 +769,7 @@ static void InputScrollPartUpdateHThumb(WindowBase& w, WidgetIndex widgetIndex,
|
|||
newLeft = 0;
|
||||
x = widget.width() - 1;
|
||||
if (scroll.flags & VSCROLLBAR_VISIBLE)
|
||||
x -= SCROLLBAR_WIDTH + 1;
|
||||
x -= kScrollBarWidth + 1;
|
||||
x *= -1;
|
||||
x += scroll.h_right;
|
||||
if (x < 0)
|
||||
|
@ -796,7 +798,7 @@ static void InputScrollPartUpdateVThumb(WindowBase& w, WidgetIndex widgetIndex,
|
|||
newTop *= y;
|
||||
y = widget.height() - 21;
|
||||
if (scroll.flags & HSCROLLBAR_VISIBLE)
|
||||
y -= SCROLLBAR_WIDTH + 1;
|
||||
y -= kScrollBarWidth + 1;
|
||||
newTop /= y;
|
||||
y = newTop;
|
||||
scroll.flags |= VSCROLLBAR_THUMB_PRESSED;
|
||||
|
@ -806,7 +808,7 @@ static void InputScrollPartUpdateVThumb(WindowBase& w, WidgetIndex widgetIndex,
|
|||
newTop = 0;
|
||||
y = widget.height() - 1;
|
||||
if (scroll.flags & HSCROLLBAR_VISIBLE)
|
||||
y -= SCROLLBAR_WIDTH + 1;
|
||||
y -= kScrollBarWidth + 1;
|
||||
y *= -1;
|
||||
y += scroll.v_bottom;
|
||||
if (y < 0)
|
||||
|
@ -850,7 +852,7 @@ static void InputScrollPartUpdateHRight(WindowBase& w, WidgetIndex widgetIndex,
|
|||
scroll.h_left += 3;
|
||||
int32_t newLeft = widget.width() - 1;
|
||||
if (scroll.flags & VSCROLLBAR_VISIBLE)
|
||||
newLeft -= SCROLLBAR_WIDTH + 1;
|
||||
newLeft -= kScrollBarWidth + 1;
|
||||
newLeft *= -1;
|
||||
newLeft += scroll.h_right;
|
||||
if (newLeft < 0)
|
||||
|
@ -893,7 +895,7 @@ static void InputScrollPartUpdateVBottom(WindowBase& w, WidgetIndex widgetIndex,
|
|||
scroll.v_top += 3;
|
||||
int32_t newTop = widget.height() - 1;
|
||||
if (scroll.flags & HSCROLLBAR_VISIBLE)
|
||||
newTop -= SCROLLBAR_WIDTH + 1;
|
||||
newTop -= kScrollBarWidth + 1;
|
||||
newTop *= -1;
|
||||
newTop += scroll.v_bottom;
|
||||
if (newTop < 0)
|
||||
|
@ -938,7 +940,7 @@ static void InputWidgetOver(const ScreenCoordsXY& screenCoords, WindowBase* w, W
|
|||
WindowTooltipClose();
|
||||
else
|
||||
{
|
||||
WindowEventScrollMouseoverCall(w, scrollId, newScreenCoords);
|
||||
w->OnScrollMouseOver(scrollId, newScreenCoords);
|
||||
InputUpdateTooltip(w, widgetIndex, screenCoords);
|
||||
}
|
||||
}
|
||||
|
@ -985,7 +987,7 @@ static void InputWidgetOverFlatbuttonInvalidate()
|
|||
WindowBase* w = WindowFindByNumber(gHoverWidget.window_classification, gHoverWidget.window_number);
|
||||
if (w != nullptr)
|
||||
{
|
||||
WindowEventOnPrepareDrawCall(w);
|
||||
w->OnPrepareDraw();
|
||||
if (w->widgets[gHoverWidget.widget_index].type == WindowWidgetType::FlatBtn)
|
||||
{
|
||||
WidgetInvalidateByNumber(gHoverWidget.window_classification, gHoverWidget.window_number, gHoverWidget.widget_index);
|
||||
|
@ -1047,7 +1049,7 @@ static void InputWidgetLeft(const ScreenCoordsXY& screenCoords, WindowBase* w, W
|
|||
if (w != nullptr)
|
||||
{
|
||||
InputSetFlag(INPUT_FLAG_4, true);
|
||||
WindowEventToolDownCall(w, gCurrentToolWidget.widget_index, screenCoords);
|
||||
w->OnToolDown(gCurrentToolWidget.widget_index, screenCoords);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -1091,7 +1093,7 @@ static void InputWidgetLeft(const ScreenCoordsXY& screenCoords, WindowBase* w, W
|
|||
_clickRepeatTicks = gCurrentRealTimeTicks;
|
||||
|
||||
WidgetInvalidateByNumber(windowClass, windowNumber, widgetIndex);
|
||||
WindowEventMouseDownCall(w, widgetIndex);
|
||||
w->OnMouseDown(widgetIndex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -1162,13 +1164,13 @@ void ProcessMouseOver(const ScreenCoordsXY& screenCoords)
|
|||
break;
|
||||
}
|
||||
// Same as default but with scroll_x/y
|
||||
cursorId = WindowEventCursorCall(window, widgetId, scrollCoords);
|
||||
cursorId = window->OnCursor(widgetId, scrollCoords, CursorID::Arrow);
|
||||
if (cursorId == CursorID::Undefined)
|
||||
cursorId = CursorID::Arrow;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
cursorId = WindowEventCursorCall(window, widgetId, screenCoords);
|
||||
cursorId = window->OnCursor(widgetId, screenCoords, CursorID::Arrow);
|
||||
if (cursorId == CursorID::Undefined)
|
||||
cursorId = CursorID::Arrow;
|
||||
break;
|
||||
|
@ -1193,7 +1195,7 @@ void ProcessMouseTool(const ScreenCoordsXY& screenCoords)
|
|||
if (w == nullptr)
|
||||
ToolCancel();
|
||||
else if (InputGetState() != InputState::ViewportRight)
|
||||
WindowEventToolUpdateCall(w, gCurrentToolWidget.widget_index, screenCoords);
|
||||
w->OnToolUpdate(gCurrentToolWidget.widget_index, screenCoords);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1323,7 +1325,7 @@ void InputStateWidgetPressed(
|
|||
{
|
||||
if (WidgetIsHoldable(*w, widgetIndex))
|
||||
{
|
||||
WindowEventMouseDownCall(w, widgetIndex);
|
||||
w->OnMouseDown(widgetIndex);
|
||||
}
|
||||
|
||||
// Subtract initial delay from here on we want the event each third tick.
|
||||
|
@ -1413,7 +1415,7 @@ void InputStateWidgetPressed(
|
|||
dropdown_index = gDropdownDefaultIndex;
|
||||
}
|
||||
}
|
||||
WindowEventDropdownCall(cursor_w, cursor_widgetIndex, dropdown_index);
|
||||
cursor_w->OnDropdown(cursor_widgetIndex, dropdown_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1445,7 +1447,7 @@ void InputStateWidgetPressed(
|
|||
break;
|
||||
|
||||
WidgetInvalidateByNumber(cursor_w_class, cursor_w_number, widgetIndex);
|
||||
WindowEventMouseUpCall(w, widgetIndex);
|
||||
w->OnMouseUp(widgetIndex);
|
||||
return;
|
||||
|
||||
default:
|
||||
|
@ -1688,18 +1690,18 @@ void InputScrollViewport(const ScreenCoordsXY& scrollScreenCoords)
|
|||
int32_t y = mainWindow->savedViewPos.y + viewport->view_height / 2;
|
||||
int32_t y_dy = mainWindow->savedViewPos.y + viewport->view_height / 2 + dy;
|
||||
|
||||
auto mapCoord = ViewportPosToMapPos({ x, y }, 0);
|
||||
auto mapCoord_dy = ViewportPosToMapPos({ x, y_dy }, 0);
|
||||
auto mapCoord = ViewportPosToMapPos({ x, y }, 0, viewport->rotation);
|
||||
auto mapCoord_dy = ViewportPosToMapPos({ x, y_dy }, 0, viewport->rotation);
|
||||
|
||||
// Check if we're crossing the boundary
|
||||
// Clamp to the map minimum value
|
||||
int32_t at_map_edge = 0;
|
||||
int32_t at_map_edge_dy = 0;
|
||||
if (mapCoord.x < MAP_MINIMUM_X_Y || mapCoord.y < MAP_MINIMUM_X_Y)
|
||||
if (mapCoord.x < kMapMinimumXY || mapCoord.y < kMapMinimumXY)
|
||||
{
|
||||
at_map_edge = 1;
|
||||
}
|
||||
if (mapCoord_dy.x < MAP_MINIMUM_X_Y || mapCoord_dy.y < MAP_MINIMUM_X_Y)
|
||||
if (mapCoord_dy.x < kMapMinimumXY || mapCoord_dy.y < kMapMinimumXY)
|
||||
{
|
||||
at_map_edge_dy = 1;
|
||||
}
|
||||
|
|
|
@ -108,6 +108,7 @@ namespace OpenRCT2::Ui::ShortcutId
|
|||
constexpr std::string_view WindowTileInspectorToggleInvisibility = "window.tileinspector.toggle_invisibility";
|
||||
constexpr std::string_view WindowTileInspectorCopy = "window.tileinspector.copy";
|
||||
constexpr std::string_view WindowTileInspectorPaste = "window.tileinspector.paste";
|
||||
constexpr std::string_view WindowTileInspectorSort = "window.tileinspector.sort";
|
||||
constexpr std::string_view WindowTileInspectorRemove = "window.tileinspector.remove";
|
||||
constexpr std::string_view WindowTileInspectorMoveUp = "window.tileinspector.move_up";
|
||||
constexpr std::string_view WindowTileInspectorMoveDown = "window.tileinspector.move_down";
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <openrct2/interface/Screenshot.h>
|
||||
#include <openrct2/localisation/Localisation.h>
|
||||
#include <openrct2/network/network.h>
|
||||
#include <openrct2/object/WallSceneryEntry.h>
|
||||
#include <openrct2/platform/Platform.h>
|
||||
#include <openrct2/ride/Track.h>
|
||||
#include <openrct2/ride/TrackPaint.h>
|
||||
|
@ -47,6 +48,7 @@
|
|||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Ui;
|
||||
using namespace OpenRCT2::Ui::Windows;
|
||||
|
||||
#pragma region Shortcut Commands
|
||||
|
||||
|
@ -62,11 +64,7 @@ static void RotateCamera(int32_t direction)
|
|||
{
|
||||
if (!(gScreenFlags & SCREEN_FLAGS_TITLE_DEMO))
|
||||
{
|
||||
auto window = WindowGetMain();
|
||||
if (window != nullptr)
|
||||
{
|
||||
WindowRotateCamera(*window, direction);
|
||||
}
|
||||
ViewportRotateAll(direction);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +91,7 @@ static void ShortcutRotateConstructionObject()
|
|||
if (w != nullptr && !WidgetIsDisabled(*w, WC_SCENERY__WIDX_SCENERY_ROTATE_OBJECTS_BUTTON)
|
||||
&& w->widgets[WC_SCENERY__WIDX_SCENERY_ROTATE_OBJECTS_BUTTON].type != WindowWidgetType::Empty)
|
||||
{
|
||||
WindowEventMouseUpCall(w, WC_SCENERY__WIDX_SCENERY_ROTATE_OBJECTS_BUTTON);
|
||||
w->OnMouseUp(WC_SCENERY__WIDX_SCENERY_ROTATE_OBJECTS_BUTTON);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -105,7 +103,7 @@ static void ShortcutRotateConstructionObject()
|
|||
// Check if building a maze...
|
||||
if (w->widgets[WC_RIDE_CONSTRUCTION__WIDX_ROTATE].tooltip != STR_RIDE_CONSTRUCTION_BUILD_MAZE_IN_THIS_DIRECTION_TIP)
|
||||
{
|
||||
WindowEventMouseUpCall(w, WC_RIDE_CONSTRUCTION__WIDX_ROTATE);
|
||||
w->OnMouseUp(WC_RIDE_CONSTRUCTION__WIDX_ROTATE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +113,7 @@ static void ShortcutRotateConstructionObject()
|
|||
if (w != nullptr && !WidgetIsDisabled(*w, WC_TRACK_DESIGN_LIST__WIDX_ROTATE)
|
||||
&& w->widgets[WC_TRACK_DESIGN_LIST__WIDX_ROTATE].type != WindowWidgetType::Empty)
|
||||
{
|
||||
WindowEventMouseUpCall(w, WC_TRACK_DESIGN_LIST__WIDX_ROTATE);
|
||||
w->OnMouseUp(WC_TRACK_DESIGN_LIST__WIDX_ROTATE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -124,7 +122,7 @@ static void ShortcutRotateConstructionObject()
|
|||
if (w != nullptr && !WidgetIsDisabled(*w, WC_TRACK_DESIGN_PLACE__WIDX_ROTATE)
|
||||
&& w->widgets[WC_TRACK_DESIGN_PLACE__WIDX_ROTATE].type != WindowWidgetType::Empty)
|
||||
{
|
||||
WindowEventMouseUpCall(w, WC_TRACK_DESIGN_PLACE__WIDX_ROTATE);
|
||||
w->OnMouseUp(WC_TRACK_DESIGN_PLACE__WIDX_ROTATE);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -133,7 +131,7 @@ static void ShortcutRotateConstructionObject()
|
|||
if (w != nullptr && !WidgetIsDisabled(*w, WC_MAP__WIDX_ROTATE_90)
|
||||
&& w->widgets[WC_MAP__WIDX_ROTATE_90].type != WindowWidgetType::Empty)
|
||||
{
|
||||
WindowEventMouseUpCall(w, WC_MAP__WIDX_ROTATE_90);
|
||||
w->OnMouseUp(WC_MAP__WIDX_ROTATE_90);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -142,7 +140,7 @@ static void ShortcutRotateConstructionObject()
|
|||
if (w != nullptr && !WidgetIsDisabled(*w, WC_TILE_INSPECTOR__WIDX_BUTTON_ROTATE)
|
||||
&& w->widgets[WC_TILE_INSPECTOR__WIDX_BUTTON_ROTATE].type != WindowWidgetType::Empty)
|
||||
{
|
||||
WindowEventMouseUpCall(w, WC_TILE_INSPECTOR__WIDX_BUTTON_ROTATE);
|
||||
w->OnMouseUp(WC_TILE_INSPECTOR__WIDX_BUTTON_ROTATE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -202,7 +200,7 @@ static void ShortcutAdjustLand()
|
|||
if (window != nullptr)
|
||||
{
|
||||
window->Invalidate();
|
||||
WindowEventMouseUpCall(window, WC_TOP_TOOLBAR__WIDX_LAND);
|
||||
window->OnMouseUp(WC_TOP_TOOLBAR__WIDX_LAND);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -221,7 +219,7 @@ static void ShortcutAdjustWater()
|
|||
if (window != nullptr)
|
||||
{
|
||||
window->Invalidate();
|
||||
WindowEventMouseUpCall(window, WC_TOP_TOOLBAR__WIDX_WATER);
|
||||
window->OnMouseUp(WC_TOP_TOOLBAR__WIDX_WATER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -240,7 +238,7 @@ static void ShortcutBuildScenery()
|
|||
if (window != nullptr)
|
||||
{
|
||||
window->Invalidate();
|
||||
WindowEventMouseUpCall(window, WC_TOP_TOOLBAR__WIDX_SCENERY);
|
||||
window->OnMouseUp(WC_TOP_TOOLBAR__WIDX_SCENERY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -393,7 +391,7 @@ static void ShortcutOpenCheatWindow()
|
|||
|
||||
static void ShortcutOpenKeyboardShortcutsWindow()
|
||||
{
|
||||
WindowShortcutKeysOpen();
|
||||
ShortcutKeysOpen();
|
||||
}
|
||||
|
||||
static void ShortcutOpenTransparencyWindow()
|
||||
|
@ -417,7 +415,7 @@ static void ShortcutClearScenery()
|
|||
if (window != nullptr)
|
||||
{
|
||||
window->Invalidate();
|
||||
WindowEventMouseUpCall(window, WC_TOP_TOOLBAR__WIDX_CLEAR_SCENERY);
|
||||
window->OnMouseUp(WC_TOP_TOOLBAR__WIDX_CLEAR_SCENERY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -462,16 +460,15 @@ static void ShortcutOpenSceneryPicker()
|
|||
if (window_toolbar != nullptr)
|
||||
{
|
||||
window_toolbar->Invalidate();
|
||||
WindowEventMouseUpCall(window_toolbar, WC_TOP_TOOLBAR__WIDX_SCENERY);
|
||||
window_toolbar->OnMouseUp(WC_TOP_TOOLBAR__WIDX_SCENERY);
|
||||
}
|
||||
}
|
||||
|
||||
window_scenery = WindowFindByClass(WindowClass::Scenery);
|
||||
if (window_scenery != nullptr && !WidgetIsDisabled(*window_scenery, WC_SCENERY__WIDX_SCENERY_EYEDROPPER_BUTTON)
|
||||
&& window_scenery->widgets[WC_SCENERY__WIDX_SCENERY_EYEDROPPER_BUTTON].type != WindowWidgetType::Empty
|
||||
&& !gWindowSceneryEyedropperEnabled)
|
||||
{
|
||||
WindowEventMouseUpCall(window_scenery, WC_SCENERY__WIDX_SCENERY_EYEDROPPER_BUTTON);
|
||||
window_scenery->OnMouseUp(WC_SCENERY__WIDX_SCENERY_EYEDROPPER_BUTTON);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -501,7 +498,7 @@ static void TileInspectorMouseUp(WidgetIndex widgetIndex)
|
|||
auto w = WindowFindByClass(WindowClass::TileInspector);
|
||||
if (w != nullptr && !WidgetIsDisabled(*w, widgetIndex) && w->widgets[widgetIndex].type != WindowWidgetType::Empty)
|
||||
{
|
||||
WindowEventMouseUpCall(w, widgetIndex);
|
||||
w->OnMouseUp(widgetIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -510,7 +507,7 @@ static void TileInspectorMouseDown(WidgetIndex widgetIndex)
|
|||
auto w = WindowFindByClass(WindowClass::TileInspector);
|
||||
if (w != nullptr && !WidgetIsDisabled(*w, widgetIndex) && w->widgets[widgetIndex].type != WindowWidgetType::Empty)
|
||||
{
|
||||
WindowEventMouseDownCall(w, widgetIndex);
|
||||
w->OnMouseDown(widgetIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -530,6 +527,12 @@ static void ShortcutToggleWallSlope()
|
|||
return;
|
||||
}
|
||||
|
||||
// Ensure a wall can be built on a slope
|
||||
if (tileElement->AsWall()->GetEntry()->flags & WALL_SCENERY_CANT_BUILD_ON_SLOPE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t currSlopeValue = tileElement->AsWall()->GetSlope();
|
||||
int32_t newSlopeValue = (currSlopeValue + 1) % 3;
|
||||
|
||||
|
@ -573,7 +576,7 @@ static void ShortcutIncreaseElementHeight()
|
|||
break;
|
||||
}
|
||||
if (action != -1 && !WidgetIsDisabled(*w, action) && w->widgets[action].type != WindowWidgetType::Empty)
|
||||
WindowEventMouseDownCall(w, action);
|
||||
w->OnMouseDown(action);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -612,14 +615,15 @@ static void ShortcutDecreaseElementHeight()
|
|||
break;
|
||||
}
|
||||
if (action != -1 && !WidgetIsDisabled(*w, action) && w->widgets[action].type != WindowWidgetType::Empty)
|
||||
WindowEventMouseDownCall(w, action);
|
||||
w->OnMouseDown(action);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void ShortcutToggleClearanceChecks()
|
||||
{
|
||||
auto cheatSetAction = CheatSetAction(CheatType::DisableClearanceChecks, gCheatsDisableClearanceChecks ? 0 : 1);
|
||||
auto cheatSetAction = CheatSetAction(
|
||||
CheatType::DisableClearanceChecks, GetGameState().Cheats.DisableClearanceChecks ? 0 : 1);
|
||||
GameActions::Execute(&cheatSetAction);
|
||||
}
|
||||
|
||||
|
@ -878,6 +882,7 @@ void ShortcutManager::RegisterDefaultShortcuts()
|
|||
RegisterShortcut(ShortcutId::WindowTileInspectorToggleInvisibility, STR_SHORTCUT_TOGGLE_INVISIBILITY, WindowTileInspectorKeyboardShortcutToggleInvisibility);
|
||||
RegisterShortcut(ShortcutId::WindowTileInspectorCopy, STR_SHORTCUT_COPY_ELEMENT, std::bind(TileInspectorMouseUp, WC_TILE_INSPECTOR__WIDX_BUTTON_COPY));
|
||||
RegisterShortcut(ShortcutId::WindowTileInspectorPaste, STR_SHORTCUT_PASTE_ELEMENT, std::bind(TileInspectorMouseUp, WC_TILE_INSPECTOR__WIDX_BUTTON_PASTE));
|
||||
RegisterShortcut(ShortcutId::WindowTileInspectorSort, STR_SHORTCUT_SORT_ELEMENTS, std::bind(TileInspectorMouseUp, WC_TILE_INSPECTOR__WIDX_BUTTON_SORT));
|
||||
RegisterShortcut(ShortcutId::WindowTileInspectorRemove, STR_SHORTCUT_REMOVE_ELEMENT, std::bind(TileInspectorMouseUp, WC_TILE_INSPECTOR__WIDX_BUTTON_REMOVE));
|
||||
RegisterShortcut(ShortcutId::WindowTileInspectorMoveUp, STR_SHORTCUT_MOVE_ELEMENT_UP, std::bind(TileInspectorMouseUp, WC_TILE_INSPECTOR__WIDX_BUTTON_MOVE_UP));
|
||||
RegisterShortcut(ShortcutId::WindowTileInspectorMoveDown, STR_SHORTCUT_MOVE_ELEMENT_DOWN, std::bind(TileInspectorMouseUp, WC_TILE_INSPECTOR__WIDX_BUTTON_MOVE_DOWN));
|
||||
|
|
|
@ -37,29 +37,33 @@ namespace Dropdown
|
|||
void SetImage(int32_t index, ImageId image);
|
||||
} // namespace Dropdown
|
||||
|
||||
extern int32_t gDropdownNumItems;
|
||||
extern Dropdown::Item gDropdownItems[Dropdown::ItemsMaxSize];
|
||||
extern bool gDropdownIsColour;
|
||||
extern int32_t gDropdownLastColourHover;
|
||||
extern int32_t gDropdownHighlightedIndex;
|
||||
extern int32_t gDropdownDefaultIndex;
|
||||
namespace OpenRCT2::Ui::Windows
|
||||
{
|
||||
extern int32_t gDropdownNumItems;
|
||||
extern Dropdown::Item gDropdownItems[Dropdown::ItemsMaxSize];
|
||||
extern bool gDropdownIsColour;
|
||||
extern int32_t gDropdownLastColourHover;
|
||||
extern int32_t gDropdownHighlightedIndex;
|
||||
extern int32_t gDropdownDefaultIndex;
|
||||
|
||||
void WindowDropdownShowText(const ScreenCoordsXY& screenPos, int32_t extray, uint8_t colour, uint8_t flags, size_t num_items);
|
||||
void WindowDropdownShowTextCustomWidth(
|
||||
const ScreenCoordsXY& screenPos, int32_t extray, uint8_t colour, uint8_t custom_height, uint8_t flags, size_t num_items,
|
||||
int32_t width);
|
||||
void WindowDropdownShowImage(
|
||||
int32_t x, int32_t y, int32_t extray, uint8_t colour, uint8_t flags, int32_t numItems, int32_t itemWidth,
|
||||
int32_t itemHeight, int32_t numColumns);
|
||||
void WindowDropdownClose();
|
||||
int32_t DropdownIndexFromPoint(const ScreenCoordsXY& loc, WindowBase* w);
|
||||
void WindowDropdownShowColour(WindowBase* w, Widget* widget, uint8_t dropdownColour, uint8_t selectedColour);
|
||||
void WindowDropdownShowColourAvailable(
|
||||
WindowBase* w, Widget* widget, uint8_t dropdownColour, uint8_t selectedColour, uint32_t availableColours);
|
||||
uint32_t DropdownGetAppropriateImageDropdownItemsPerRow(uint32_t numItems);
|
||||
bool WindowDropDownHasMultipleColumns(size_t numItems);
|
||||
void WindowDropdownShowText(
|
||||
const ScreenCoordsXY& screenPos, int32_t extray, uint8_t colour, uint8_t flags, size_t num_items);
|
||||
void WindowDropdownShowTextCustomWidth(
|
||||
const ScreenCoordsXY& screenPos, int32_t extray, uint8_t colour, uint8_t custom_height, uint8_t flags, size_t num_items,
|
||||
int32_t width);
|
||||
void WindowDropdownShowImage(
|
||||
int32_t x, int32_t y, int32_t extray, uint8_t colour, uint8_t flags, int32_t numItems, int32_t itemWidth,
|
||||
int32_t itemHeight, int32_t numColumns);
|
||||
void WindowDropdownClose();
|
||||
int32_t DropdownIndexFromPoint(const ScreenCoordsXY& loc, WindowBase* w);
|
||||
void WindowDropdownShowColour(WindowBase* w, Widget* widget, uint8_t dropdownColour, uint8_t selectedColour);
|
||||
void WindowDropdownShowColourAvailable(
|
||||
WindowBase* w, Widget* widget, uint8_t dropdownColour, uint8_t selectedColour, uint32_t availableColours);
|
||||
uint32_t DropdownGetAppropriateImageDropdownItemsPerRow(uint32_t numItems);
|
||||
bool WindowDropDownHasMultipleColumns(size_t numItems);
|
||||
|
||||
colour_t ColourDropDownIndexToColour(uint8_t ddidx);
|
||||
colour_t ColourDropDownIndexToColour(uint8_t ddidx);
|
||||
} // namespace OpenRCT2::Ui::Windows
|
||||
|
||||
namespace Dropdown
|
||||
{
|
||||
|
@ -120,8 +124,8 @@ namespace Dropdown
|
|||
for (int i = 0; i < N; ++i)
|
||||
{
|
||||
const ItemExt& item = items[i];
|
||||
gDropdownItems[i].Format = item.itemFormat;
|
||||
gDropdownItems[i].Args = item.stringId;
|
||||
OpenRCT2::Ui::Windows::gDropdownItems[i].Format = item.itemFormat;
|
||||
OpenRCT2::Ui::Windows::gDropdownItems[i].Args = item.stringId;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <openrct2/interface/Window.h>
|
||||
#include <openrct2/localisation/Language.h>
|
||||
#include <openrct2/localisation/LocalisationService.h>
|
||||
#include <openrct2/localisation/StringIds.h>
|
||||
|
||||
using namespace OpenRCT2::Ui;
|
||||
|
||||
|
@ -36,10 +37,10 @@ static int32_t InGameConsoleGetLineHeight()
|
|||
return FontGetLineHeight(InGameConsoleGetFontStyle());
|
||||
}
|
||||
|
||||
InGameConsole::InGameConsole()
|
||||
void InGameConsole::WriteInitial()
|
||||
{
|
||||
InteractiveConsole::WriteLine(OPENRCT2_NAME " " OPENRCT2_VERSION);
|
||||
InteractiveConsole::WriteLine("Type 'help' for a list of available commands. Type 'hide' to hide the console.");
|
||||
InteractiveConsole::WriteLine(LanguageGetString(STR_CONSOLE_HELPER_TEXT));
|
||||
InteractiveConsole::WriteLine("");
|
||||
WritePrompt();
|
||||
}
|
||||
|
@ -176,6 +177,12 @@ void InGameConsole::ClearLine()
|
|||
|
||||
void InGameConsole::Open()
|
||||
{
|
||||
if (!_isInitialised)
|
||||
{
|
||||
WriteInitial();
|
||||
_isInitialised = true;
|
||||
}
|
||||
|
||||
_isOpen = true;
|
||||
ScrollToEnd();
|
||||
RefreshCaret();
|
||||
|
|
|
@ -26,6 +26,7 @@ namespace OpenRCT2::Ui
|
|||
static constexpr int32_t CONSOLE_EDGE_PADDING = 4;
|
||||
static constexpr int32_t CONSOLE_CARET_WIDTH = 6;
|
||||
|
||||
bool _isInitialised = false;
|
||||
bool _isOpen = false;
|
||||
ScreenCoordsXY _consoleTopLeft;
|
||||
ScreenCoordsXY _consoleBottomRight;
|
||||
|
@ -45,7 +46,7 @@ namespace OpenRCT2::Ui
|
|||
int32_t _caretScreenPosX = 0;
|
||||
|
||||
public:
|
||||
InGameConsole();
|
||||
InGameConsole() = default;
|
||||
InGameConsole(const InGameConsole& src) = delete;
|
||||
|
||||
bool IsOpen() const
|
||||
|
@ -71,6 +72,7 @@ namespace OpenRCT2::Ui
|
|||
void ClearInput();
|
||||
void ClearLine();
|
||||
void HistoryAdd(const u8string& src);
|
||||
void WriteInitial();
|
||||
void WritePrompt();
|
||||
void ScrollToEnd();
|
||||
void Invalidate() const;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <openrct2/world/Surface.h>
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Ui::Windows;
|
||||
|
||||
// clang-format off
|
||||
static uint16_t toolSizeSpriteIndices[] =
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2024 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "Objective.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <openrct2/localisation/Formatter.h>
|
||||
#include <openrct2/localisation/Localisation.h>
|
||||
#include <openrct2/ride/Ride.h>
|
||||
#include <openrct2/ride/RideData.h>
|
||||
#include <openrct2/scenario/Scenario.h>
|
||||
|
||||
void formatObjective(Formatter& ft, Objective objective)
|
||||
{
|
||||
if (objective.Type == OBJECTIVE_BUILD_THE_BEST)
|
||||
{
|
||||
StringId rideTypeString = STR_NONE;
|
||||
auto rideTypeId = objective.RideId;
|
||||
if (rideTypeId != RIDE_TYPE_NULL && rideTypeId < RIDE_TYPE_COUNT)
|
||||
{
|
||||
rideTypeString = GetRideTypeDescriptor(rideTypeId).Naming.Name;
|
||||
}
|
||||
ft.Add<StringId>(rideTypeString);
|
||||
}
|
||||
else if (objective.Type == OBJECTIVE_GUESTS_BY)
|
||||
{
|
||||
ft.Add<int32_t>(objective.NumGuests);
|
||||
ft.Add<int16_t>(DateGetTotalMonths(MONTH_OCTOBER, objective.Year));
|
||||
}
|
||||
else if (objective.Type == OBJECTIVE_GUESTS_AND_RATING)
|
||||
{
|
||||
ft.Add<int32_t>(objective.NumGuests);
|
||||
}
|
||||
else if (objective.Type == OBJECTIVE_10_ROLLERCOASTERS_LENGTH)
|
||||
{
|
||||
ft.Add<int16_t>(objective.MinimumLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
ft.Add<int16_t>(objective.NumGuests);
|
||||
ft.Add<int16_t>(DateGetTotalMonths(MONTH_OCTOBER, objective.Year));
|
||||
if (objective.Type == OBJECTIVE_FINISH_5_ROLLERCOASTERS)
|
||||
ft.Add<uint16_t>(objective.MinimumExcitement);
|
||||
else
|
||||
ft.Add<money64>(objective.Currency);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2024 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class Formatter;
|
||||
struct Objective;
|
||||
|
||||
void formatObjective(Formatter& ft, Objective objective);
|
|
@ -53,6 +53,7 @@
|
|||
#include <openrct2/world/Wall.h>
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Ui::Windows;
|
||||
|
||||
static void ViewportInteractionRemoveScenery(const SmallSceneryElement& smallSceneryElement, const CoordsXY& mapCoords);
|
||||
static void ViewportInteractionRemoveFootpath(const PathElement& pathElement, const CoordsXY& mapCoords);
|
||||
|
@ -66,7 +67,7 @@ static Peep* ViewportInteractionGetClosestPeep(ScreenCoordsXY screenCoords, int3
|
|||
*
|
||||
* rct2: 0x006ED9D0
|
||||
*/
|
||||
InteractionInfo ViewportInteractionGetItemLeft(const ScreenCoordsXY& screenCoords)
|
||||
static InteractionInfo ViewportInteractionGetItemLeft(const ScreenCoordsXY& screenCoords)
|
||||
{
|
||||
InteractionInfo info{};
|
||||
// No click input for scenario editor or track manager
|
||||
|
@ -250,7 +251,7 @@ bool ViewportInteractionLeftClick(const ScreenCoordsXY& screenCoords)
|
|||
*
|
||||
* rct2: 0x006EDE88
|
||||
*/
|
||||
InteractionInfo ViewportInteractionGetItemRight(const ScreenCoordsXY& screenCoords)
|
||||
static InteractionInfo ViewportInteractionGetItemRight(const ScreenCoordsXY& screenCoords)
|
||||
{
|
||||
Ride* ride;
|
||||
int32_t i;
|
||||
|
@ -364,7 +365,7 @@ InteractionInfo ViewportInteractionGetItemRight(const ScreenCoordsXY& screenCoor
|
|||
else
|
||||
{
|
||||
// FIXME: Why does it *2 the value?
|
||||
if (!gCheatsSandboxMode && !MapIsLocationOwned({ info.Loc, tileElement->GetBaseZ() * 2 }))
|
||||
if (!GetGameState().Cheats.SandboxMode && !MapIsLocationOwned({ info.Loc, tileElement->GetBaseZ() * 2 }))
|
||||
{
|
||||
info.SpriteType = ViewportInteractionItem::None;
|
||||
return info;
|
||||
|
@ -494,7 +495,7 @@ InteractionInfo ViewportInteractionGetItemRight(const ScreenCoordsXY& screenCoor
|
|||
return info;
|
||||
}
|
||||
case ViewportInteractionItem::ParkEntrance:
|
||||
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !gCheatsSandboxMode)
|
||||
if (!(gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR) && !GetGameState().Cheats.SandboxMode)
|
||||
break;
|
||||
|
||||
if (tileElement->GetType() != TileElementType::Entrance)
|
||||
|
@ -718,16 +719,21 @@ struct PeepDistance
|
|||
};
|
||||
|
||||
template<typename T>
|
||||
PeepDistance GetClosestPeep(const ScreenCoordsXY& viewportCoords, const int32_t maxDistance, PeepDistance goal)
|
||||
static PeepDistance GetClosestPeep(
|
||||
const ScreenCoordsXY& viewportCoords, uint8_t rotation, const int32_t maxDistance, PeepDistance goal)
|
||||
{
|
||||
for (auto peep : EntityList<T>())
|
||||
{
|
||||
if (peep->x == LOCATION_NULL)
|
||||
continue;
|
||||
|
||||
auto distance = abs(((peep->SpriteData.SpriteRect.GetLeft() + peep->SpriteData.SpriteRect.GetRight()) / 2)
|
||||
- viewportCoords.x)
|
||||
+ abs(((peep->SpriteData.SpriteRect.GetTop() + peep->SpriteData.SpriteRect.GetBottom()) / 2) - viewportCoords.y);
|
||||
auto screenCoords = Translate3DTo2DWithZ(rotation, peep->GetLocation());
|
||||
auto spriteRect = ScreenRect(
|
||||
screenCoords - ScreenCoordsXY{ peep->SpriteData.Width, peep->SpriteData.HeightMin },
|
||||
screenCoords + ScreenCoordsXY{ peep->SpriteData.Width, peep->SpriteData.HeightMax });
|
||||
|
||||
auto distance = abs(((spriteRect.GetLeft() + spriteRect.GetRight()) / 2) - viewportCoords.x)
|
||||
+ abs(((spriteRect.GetTop() + spriteRect.GetBottom()) / 2) - viewportCoords.y);
|
||||
if (distance > maxDistance)
|
||||
continue;
|
||||
|
||||
|
@ -754,9 +760,9 @@ static Peep* ViewportInteractionGetClosestPeep(ScreenCoordsXY screenCoords, int3
|
|||
|
||||
PeepDistance goal;
|
||||
if (!(viewport->flags & VIEWPORT_FLAG_HIDE_GUESTS))
|
||||
goal = GetClosestPeep<Guest>(viewportCoords, maxDistance, goal);
|
||||
goal = GetClosestPeep<Guest>(viewportCoords, viewport->rotation, maxDistance, goal);
|
||||
if (!(viewport->flags & VIEWPORT_FLAG_HIDE_STAFF))
|
||||
goal = GetClosestPeep<Staff>(viewportCoords, maxDistance, goal);
|
||||
goal = GetClosestPeep<Staff>(viewportCoords, viewport->rotation, maxDistance, goal);
|
||||
return goal.peep;
|
||||
}
|
||||
|
||||
|
@ -800,7 +806,7 @@ CoordsXY ViewportInteractionGetTileStartAtCursor(const ScreenCoordsXY& screenCoo
|
|||
{
|
||||
z = TileElementHeight(mapPos);
|
||||
}
|
||||
mapPos = ViewportPosToMapPos(initialVPPos, z);
|
||||
mapPos = ViewportPosToMapPos(initialVPPos, z, viewport->rotation);
|
||||
mapPos.x = std::clamp(mapPos.x, initialPos.x, initialPos.x + 31);
|
||||
mapPos.y = std::clamp(mapPos.y, initialPos.y, initialPos.y + 31);
|
||||
}
|
||||
|
|
|
@ -687,21 +687,21 @@ static void WidgetScrollDraw(DrawPixelInfo& dpi, WindowBase& w, WidgetIndex widg
|
|||
// Horizontal scrollbar
|
||||
if (scroll.flags & HSCROLLBAR_VISIBLE)
|
||||
WidgetHScrollbarDraw(
|
||||
dpi, scroll, topLeft.x, bottomRight.y - SCROLLBAR_WIDTH,
|
||||
((scroll.flags & VSCROLLBAR_VISIBLE) ? bottomRight.x - (SCROLLBAR_WIDTH + 1) : bottomRight.x), bottomRight.y,
|
||||
dpi, scroll, topLeft.x, bottomRight.y - kScrollBarWidth,
|
||||
((scroll.flags & VSCROLLBAR_VISIBLE) ? bottomRight.x - (kScrollBarWidth + 1) : bottomRight.x), bottomRight.y,
|
||||
colour);
|
||||
|
||||
// Vertical scrollbar
|
||||
if (scroll.flags & VSCROLLBAR_VISIBLE)
|
||||
WidgetVScrollbarDraw(
|
||||
dpi, scroll, bottomRight.x - SCROLLBAR_WIDTH, topLeft.y, bottomRight.x,
|
||||
((scroll.flags & HSCROLLBAR_VISIBLE) ? bottomRight.y - (SCROLLBAR_WIDTH + 1) : bottomRight.y), colour);
|
||||
dpi, scroll, bottomRight.x - kScrollBarWidth, topLeft.y, bottomRight.x,
|
||||
((scroll.flags & HSCROLLBAR_VISIBLE) ? bottomRight.y - (kScrollBarWidth + 1) : bottomRight.y), colour);
|
||||
|
||||
// Contents
|
||||
if (scroll.flags & HSCROLLBAR_VISIBLE)
|
||||
bottomRight.y -= (SCROLLBAR_WIDTH + 1);
|
||||
bottomRight.y -= (kScrollBarWidth + 1);
|
||||
if (scroll.flags & VSCROLLBAR_VISIBLE)
|
||||
bottomRight.x -= (SCROLLBAR_WIDTH + 1);
|
||||
bottomRight.x -= (kScrollBarWidth + 1);
|
||||
|
||||
bottomRight.y++;
|
||||
bottomRight.x++;
|
||||
|
@ -726,7 +726,7 @@ static void WidgetScrollDraw(DrawPixelInfo& dpi, WindowBase& w, WidgetIndex widg
|
|||
|
||||
// Draw the scroll contents
|
||||
if (scroll_dpi.width > 0 && scroll_dpi.height > 0)
|
||||
WindowEventScrollDrawCall(&w, scroll_dpi, scrollIndex);
|
||||
w.OnScrollDraw(scrollIndex, scroll_dpi);
|
||||
}
|
||||
|
||||
static void WidgetHScrollbarDraw(
|
||||
|
@ -734,25 +734,25 @@ static void WidgetHScrollbarDraw(
|
|||
{
|
||||
colour &= 0x7F;
|
||||
// Trough
|
||||
GfxFillRect(dpi, { { l + SCROLLBAR_WIDTH, t }, { r - SCROLLBAR_WIDTH, b } }, ColourMapA[colour].lighter);
|
||||
GfxFillRect(dpi, { { l + SCROLLBAR_WIDTH, t }, { r - SCROLLBAR_WIDTH, b } }, 0x1000000 | ColourMapA[colour].mid_dark);
|
||||
GfxFillRect(dpi, { { l + SCROLLBAR_WIDTH, t + 2 }, { r - SCROLLBAR_WIDTH, t + 2 } }, ColourMapA[colour].mid_dark);
|
||||
GfxFillRect(dpi, { { l + SCROLLBAR_WIDTH, t + 3 }, { r - SCROLLBAR_WIDTH, t + 3 } }, ColourMapA[colour].lighter);
|
||||
GfxFillRect(dpi, { { l + SCROLLBAR_WIDTH, t + 7 }, { r - SCROLLBAR_WIDTH, t + 7 } }, ColourMapA[colour].mid_dark);
|
||||
GfxFillRect(dpi, { { l + SCROLLBAR_WIDTH, t + 8 }, { r - SCROLLBAR_WIDTH, t + 8 } }, ColourMapA[colour].lighter);
|
||||
GfxFillRect(dpi, { { l + kScrollBarWidth, t }, { r - kScrollBarWidth, b } }, ColourMapA[colour].lighter);
|
||||
GfxFillRect(dpi, { { l + kScrollBarWidth, t }, { r - kScrollBarWidth, b } }, 0x1000000 | ColourMapA[colour].mid_dark);
|
||||
GfxFillRect(dpi, { { l + kScrollBarWidth, t + 2 }, { r - kScrollBarWidth, t + 2 } }, ColourMapA[colour].mid_dark);
|
||||
GfxFillRect(dpi, { { l + kScrollBarWidth, t + 3 }, { r - kScrollBarWidth, t + 3 } }, ColourMapA[colour].lighter);
|
||||
GfxFillRect(dpi, { { l + kScrollBarWidth, t + 7 }, { r - kScrollBarWidth, t + 7 } }, ColourMapA[colour].mid_dark);
|
||||
GfxFillRect(dpi, { { l + kScrollBarWidth, t + 8 }, { r - kScrollBarWidth, t + 8 } }, ColourMapA[colour].lighter);
|
||||
|
||||
// Left button
|
||||
{
|
||||
uint8_t flags = (scroll.flags & HSCROLLBAR_LEFT_PRESSED) ? INSET_RECT_FLAG_BORDER_INSET : 0;
|
||||
|
||||
GfxFillRectInset(dpi, { { l, t }, { l + (SCROLLBAR_WIDTH - 1), b } }, colour, flags);
|
||||
GfxFillRectInset(dpi, { { l, t }, { l + (kScrollBarWidth - 1), b } }, colour, flags);
|
||||
GfxDrawString(dpi, { l + 1, t }, static_cast<const char*>(BlackLeftArrowString), {});
|
||||
}
|
||||
|
||||
// Thumb
|
||||
{
|
||||
int16_t left = std::max(l + SCROLLBAR_WIDTH, l + scroll.h_thumb_left - 1);
|
||||
int16_t right = std::min(r - SCROLLBAR_WIDTH, l + scroll.h_thumb_right - 1);
|
||||
int16_t left = std::max(l + kScrollBarWidth, l + scroll.h_thumb_left - 1);
|
||||
int16_t right = std::min(r - kScrollBarWidth, l + scroll.h_thumb_right - 1);
|
||||
uint8_t flags = (scroll.flags & HSCROLLBAR_THUMB_PRESSED) ? INSET_RECT_FLAG_BORDER_INSET : 0;
|
||||
|
||||
GfxFillRectInset(dpi, { { left, t }, { right, b } }, colour, flags);
|
||||
|
@ -762,7 +762,7 @@ static void WidgetHScrollbarDraw(
|
|||
{
|
||||
uint8_t flags = (scroll.flags & HSCROLLBAR_RIGHT_PRESSED) ? INSET_RECT_FLAG_BORDER_INSET : 0;
|
||||
|
||||
GfxFillRectInset(dpi, { { r - (SCROLLBAR_WIDTH - 1), t }, { r, b } }, colour, flags);
|
||||
GfxFillRectInset(dpi, { { r - (kScrollBarWidth - 1), t }, { r, b } }, colour, flags);
|
||||
GfxDrawString(dpi, { r - 6, t }, static_cast<const char*>(BlackRightArrowString), {});
|
||||
}
|
||||
}
|
||||
|
@ -772,31 +772,31 @@ static void WidgetVScrollbarDraw(
|
|||
{
|
||||
colour &= 0x7F;
|
||||
// Trough
|
||||
GfxFillRect(dpi, { { l, t + SCROLLBAR_WIDTH }, { r, b - SCROLLBAR_WIDTH } }, ColourMapA[colour].lighter);
|
||||
GfxFillRect(dpi, { { l, t + SCROLLBAR_WIDTH }, { r, b - SCROLLBAR_WIDTH } }, 0x1000000 | ColourMapA[colour].mid_dark);
|
||||
GfxFillRect(dpi, { { l + 2, t + SCROLLBAR_WIDTH }, { l + 2, b - SCROLLBAR_WIDTH } }, ColourMapA[colour].mid_dark);
|
||||
GfxFillRect(dpi, { { l + 3, t + SCROLLBAR_WIDTH }, { l + 3, b - SCROLLBAR_WIDTH } }, ColourMapA[colour].lighter);
|
||||
GfxFillRect(dpi, { { l + 7, t + SCROLLBAR_WIDTH }, { l + 7, b - SCROLLBAR_WIDTH } }, ColourMapA[colour].mid_dark);
|
||||
GfxFillRect(dpi, { { l + 8, t + SCROLLBAR_WIDTH }, { l + 8, b - SCROLLBAR_WIDTH } }, ColourMapA[colour].lighter);
|
||||
GfxFillRect(dpi, { { l, t + kScrollBarWidth }, { r, b - kScrollBarWidth } }, ColourMapA[colour].lighter);
|
||||
GfxFillRect(dpi, { { l, t + kScrollBarWidth }, { r, b - kScrollBarWidth } }, 0x1000000 | ColourMapA[colour].mid_dark);
|
||||
GfxFillRect(dpi, { { l + 2, t + kScrollBarWidth }, { l + 2, b - kScrollBarWidth } }, ColourMapA[colour].mid_dark);
|
||||
GfxFillRect(dpi, { { l + 3, t + kScrollBarWidth }, { l + 3, b - kScrollBarWidth } }, ColourMapA[colour].lighter);
|
||||
GfxFillRect(dpi, { { l + 7, t + kScrollBarWidth }, { l + 7, b - kScrollBarWidth } }, ColourMapA[colour].mid_dark);
|
||||
GfxFillRect(dpi, { { l + 8, t + kScrollBarWidth }, { l + 8, b - kScrollBarWidth } }, ColourMapA[colour].lighter);
|
||||
|
||||
// Up button
|
||||
GfxFillRectInset(
|
||||
dpi, { { l, t }, { r, t + (SCROLLBAR_WIDTH - 1) } }, colour,
|
||||
dpi, { { l, t }, { r, t + (kScrollBarWidth - 1) } }, colour,
|
||||
((scroll.flags & VSCROLLBAR_UP_PRESSED) ? INSET_RECT_FLAG_BORDER_INSET : 0));
|
||||
GfxDrawString(dpi, { l + 1, t - 1 }, static_cast<const char*>(BlackUpArrowString), {});
|
||||
|
||||
// Thumb
|
||||
GfxFillRectInset(
|
||||
dpi,
|
||||
{ { l, std::max(t + SCROLLBAR_WIDTH, t + scroll.v_thumb_top - 1) },
|
||||
{ r, std::min(b - SCROLLBAR_WIDTH, t + scroll.v_thumb_bottom - 1) } },
|
||||
{ { l, std::max(t + kScrollBarWidth, t + scroll.v_thumb_top - 1) },
|
||||
{ r, std::min(b - kScrollBarWidth, t + scroll.v_thumb_bottom - 1) } },
|
||||
colour, ((scroll.flags & VSCROLLBAR_THUMB_PRESSED) ? INSET_RECT_FLAG_BORDER_INSET : 0));
|
||||
|
||||
// Down button
|
||||
GfxFillRectInset(
|
||||
dpi, { { l, b - (SCROLLBAR_WIDTH - 1) }, { r, b } }, colour,
|
||||
dpi, { { l, b - (kScrollBarWidth - 1) }, { r, b } }, colour,
|
||||
((scroll.flags & VSCROLLBAR_DOWN_PRESSED) ? INSET_RECT_FLAG_BORDER_INSET : 0));
|
||||
GfxDrawString(dpi, { l + 1, b - (SCROLLBAR_WIDTH - 1) }, static_cast<const char*>(BlackDownArrowString), {});
|
||||
GfxDrawString(dpi, { l + 1, b - (kScrollBarWidth - 1) }, static_cast<const char*>(BlackDownArrowString), {});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -829,12 +829,12 @@ static void WidgetDrawImage(DrawPixelInfo& dpi, WindowBase& w, WidgetIndex widge
|
|||
// Draw greyed out (light border bottom right shadow)
|
||||
colour = w.colours[widget.colour];
|
||||
colour = ColourMapA[NOT_TRANSLUCENT(colour)].lighter;
|
||||
GfxDrawSpriteSolid(&dpi, image, screenCoords + ScreenCoordsXY{ 1, 1 }, colour);
|
||||
GfxDrawSpriteSolid(dpi, image, screenCoords + ScreenCoordsXY{ 1, 1 }, colour);
|
||||
|
||||
// Draw greyed out (dark)
|
||||
colour = w.colours[widget.colour];
|
||||
colour = ColourMapA[NOT_TRANSLUCENT(colour)].mid_light;
|
||||
GfxDrawSpriteSolid(&dpi, image, screenCoords, colour);
|
||||
GfxDrawSpriteSolid(dpi, image, screenCoords, colour);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -952,15 +952,15 @@ void WidgetScrollGetPart(
|
|||
}
|
||||
|
||||
const auto& scroll = w.scrolls[*scroll_id];
|
||||
if ((scroll.flags & HSCROLLBAR_VISIBLE) && screenCoords.y >= (w.windowPos.y + widget->bottom - (SCROLLBAR_WIDTH + 1)))
|
||||
if ((scroll.flags & HSCROLLBAR_VISIBLE) && screenCoords.y >= (w.windowPos.y + widget->bottom - (kScrollBarWidth + 1)))
|
||||
{
|
||||
// horizontal scrollbar
|
||||
int32_t rightOffset = 0;
|
||||
int32_t iteratorLeft = widget->left + w.windowPos.x + SCROLLBAR_WIDTH;
|
||||
int32_t iteratorRight = widget->right + w.windowPos.x - SCROLLBAR_WIDTH;
|
||||
int32_t iteratorLeft = widget->left + w.windowPos.x + kScrollBarWidth;
|
||||
int32_t iteratorRight = widget->right + w.windowPos.x - kScrollBarWidth;
|
||||
if (!(scroll.flags & VSCROLLBAR_VISIBLE))
|
||||
{
|
||||
rightOffset = SCROLLBAR_WIDTH + 1;
|
||||
rightOffset = kScrollBarWidth + 1;
|
||||
}
|
||||
|
||||
if (screenCoords.x <= iteratorLeft)
|
||||
|
@ -971,7 +971,7 @@ void WidgetScrollGetPart(
|
|||
{
|
||||
*output_scroll_area = SCROLL_PART_NONE;
|
||||
}
|
||||
else if (screenCoords.x >= iteratorRight + rightOffset - SCROLLBAR_WIDTH)
|
||||
else if (screenCoords.x >= iteratorRight + rightOffset - kScrollBarWidth)
|
||||
{
|
||||
*output_scroll_area = SCROLL_PART_HSCROLLBAR_RIGHT;
|
||||
}
|
||||
|
@ -988,15 +988,15 @@ void WidgetScrollGetPart(
|
|||
*output_scroll_area = SCROLL_PART_HSCROLLBAR_THUMB;
|
||||
}
|
||||
}
|
||||
else if ((scroll.flags & VSCROLLBAR_VISIBLE) && (screenCoords.x >= w.windowPos.x + widget->right - (SCROLLBAR_WIDTH + 1)))
|
||||
else if ((scroll.flags & VSCROLLBAR_VISIBLE) && (screenCoords.x >= w.windowPos.x + widget->right - (kScrollBarWidth + 1)))
|
||||
{
|
||||
// vertical scrollbar
|
||||
int32_t bottomOffset = 0;
|
||||
int32_t iteratorTop = widget->top + w.windowPos.y + SCROLLBAR_WIDTH;
|
||||
int32_t iteratorTop = widget->top + w.windowPos.y + kScrollBarWidth;
|
||||
int32_t iteratorBottom = widget->bottom + w.windowPos.y;
|
||||
if (scroll.flags & HSCROLLBAR_VISIBLE)
|
||||
{
|
||||
bottomOffset = (SCROLLBAR_WIDTH + 1);
|
||||
bottomOffset = (kScrollBarWidth + 1);
|
||||
}
|
||||
|
||||
if (screenCoords.y <= iteratorTop)
|
||||
|
@ -1007,7 +1007,7 @@ void WidgetScrollGetPart(
|
|||
{
|
||||
*output_scroll_area = SCROLL_PART_NONE;
|
||||
}
|
||||
else if (screenCoords.y >= (iteratorBottom - bottomOffset - SCROLLBAR_WIDTH))
|
||||
else if (screenCoords.y >= (iteratorBottom - bottomOffset - kScrollBarWidth))
|
||||
{
|
||||
*output_scroll_area = SCROLL_PART_VSCROLLBAR_BOTTOM;
|
||||
}
|
||||
|
|
|
@ -9,8 +9,147 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <openrct2/drawing/ImageId.hpp>
|
||||
#include "Window.h"
|
||||
|
||||
#include <openrct2/drawing/Drawing.h>
|
||||
#include <openrct2/interface/Widget.h>
|
||||
#include <openrct2/localisation/StringIds.h>
|
||||
|
||||
ImageId GetColourButtonImage(colour_t colour);
|
||||
Widget* GetWidgetByIndex(const WindowBase& w, WidgetIndex widgetIndex);
|
||||
|
||||
constexpr auto kWidgetsEnd = Widget{ WindowWidgetType::Last, 0, 0, 0, 0, 0, 0, 0 };
|
||||
constexpr auto kBarBlink = (1u << 31);
|
||||
|
||||
enum class WindowColour : uint8_t
|
||||
{
|
||||
Primary,
|
||||
Secondary,
|
||||
Tertiary,
|
||||
Quaternary,
|
||||
};
|
||||
|
||||
constexpr uint8_t kScrollBarWidth = 10;
|
||||
|
||||
constexpr ScreenSize kTabSize = { 31, 27 };
|
||||
|
||||
constexpr Widget MakeWidget(
|
||||
const ScreenCoordsXY& origin, const ScreenSize& size, WindowWidgetType type, WindowColour colour,
|
||||
uint32_t content = 0xFFFFFFFF, StringId tooltip = STR_NONE)
|
||||
{
|
||||
Widget out = {};
|
||||
out.left = origin.x;
|
||||
out.right = origin.x + size.width - 1;
|
||||
out.top = origin.y;
|
||||
out.bottom = origin.y + size.height - 1;
|
||||
out.type = type;
|
||||
out.colour = static_cast<uint8_t>(colour);
|
||||
out.content = content;
|
||||
out.tooltip = tooltip;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr Widget MakeWidget(
|
||||
const ScreenCoordsXY& origin, const ScreenSize& size, WindowWidgetType type, WindowColour colour, ImageId image,
|
||||
StringId tooltip = STR_NONE)
|
||||
{
|
||||
Widget out = {};
|
||||
out.left = origin.x;
|
||||
out.right = origin.x + size.width - 1;
|
||||
out.top = origin.y;
|
||||
out.bottom = origin.y + size.height - 1;
|
||||
out.type = type;
|
||||
out.colour = static_cast<uint8_t>(colour);
|
||||
out.image = image;
|
||||
out.tooltip = tooltip;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
constexpr Widget MakeRemapWidget(
|
||||
const ScreenCoordsXY& origin, const ScreenSize& size, WindowWidgetType type, WindowColour colour, ImageIndex content,
|
||||
StringId tooltip = STR_NONE)
|
||||
{
|
||||
return MakeWidget(origin, size, type, colour, ImageId(content, FilterPaletteID::PaletteNull), tooltip);
|
||||
}
|
||||
|
||||
constexpr Widget MakeTab(const ScreenCoordsXY& origin, StringId tooltip = STR_NONE)
|
||||
{
|
||||
const ScreenSize size = kTabSize;
|
||||
const WindowWidgetType type = WindowWidgetType::Tab;
|
||||
const WindowColour colour = WindowColour::Secondary;
|
||||
const auto content = ImageId(ImageIndexUndefined);
|
||||
|
||||
return MakeWidget(origin, size, type, colour, content, tooltip);
|
||||
}
|
||||
|
||||
// NOLINTBEGIN
|
||||
#define MakeSpinnerWidgets(...) \
|
||||
MakeWidget(__VA_ARGS__), MakeSpinnerIncreaseWidget(__VA_ARGS__), MakeSpinnerDecreaseWidget(__VA_ARGS__)
|
||||
// NOLINTEND
|
||||
|
||||
constexpr Widget MakeSpinnerDecreaseWidget(
|
||||
const ScreenCoordsXY& origin, const ScreenSize& size, [[maybe_unused]] WindowWidgetType type, WindowColour colour,
|
||||
[[maybe_unused]] uint32_t content = 0xFFFFFFFF, StringId tooltip = STR_NONE)
|
||||
{
|
||||
const int16_t xPos = origin.x + size.width - 26;
|
||||
const int16_t yPos = origin.y + 1;
|
||||
const uint16_t width = 13;
|
||||
const uint16_t height = size.height - 2;
|
||||
|
||||
return MakeWidget({ xPos, yPos }, { width, height }, WindowWidgetType::Button, colour, STR_NUMERIC_DOWN, tooltip);
|
||||
}
|
||||
|
||||
constexpr Widget MakeSpinnerIncreaseWidget(
|
||||
const ScreenCoordsXY& origin, const ScreenSize& size, [[maybe_unused]] WindowWidgetType type, WindowColour colour,
|
||||
[[maybe_unused]] uint32_t content = 0xFFFFFFFF, StringId tooltip = STR_NONE)
|
||||
{
|
||||
const int16_t xPos = origin.x + size.width - 13;
|
||||
const int16_t yPos = origin.y + 1;
|
||||
const uint16_t width = 12;
|
||||
const uint16_t height = size.height - 2;
|
||||
|
||||
return MakeWidget({ xPos, yPos }, { width, height }, WindowWidgetType::Button, colour, STR_NUMERIC_UP, tooltip);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
#define MakeDropdownWidgets(...) MakeDropdownBoxWidget(__VA_ARGS__), MakeDropdownButtonWidget(__VA_ARGS__)
|
||||
|
||||
constexpr Widget MakeDropdownBoxWidget(
|
||||
const ScreenCoordsXY& origin, const ScreenSize& size, [[maybe_unused]] WindowWidgetType type, WindowColour colour,
|
||||
[[maybe_unused]] uint32_t content = 0xFFFFFFFF, StringId tooltip = STR_NONE)
|
||||
{
|
||||
return MakeWidget(origin, size, type, colour, content);
|
||||
}
|
||||
|
||||
constexpr Widget MakeDropdownButtonWidget(
|
||||
const ScreenCoordsXY& origin, const ScreenSize& size, [[maybe_unused]] WindowWidgetType type, WindowColour colour,
|
||||
[[maybe_unused]] uint32_t content = 0xFFFFFFFF, StringId tooltip = STR_NONE)
|
||||
{
|
||||
const int16_t xPos = origin.x + size.width - 11;
|
||||
const int16_t yPos = origin.y + 1;
|
||||
const uint16_t width = 11;
|
||||
const uint16_t height = 10;
|
||||
|
||||
return MakeWidget({ xPos, yPos }, { width, height }, WindowWidgetType::Button, colour, STR_DROPDOWN_GLYPH, tooltip);
|
||||
}
|
||||
|
||||
void WidgetDraw(DrawPixelInfo& dpi, WindowBase& w, WidgetIndex widgetIndex);
|
||||
|
||||
bool WidgetIsDisabled(const WindowBase& w, WidgetIndex widgetIndex);
|
||||
bool WidgetIsHoldable(const WindowBase& w, WidgetIndex widgetIndex);
|
||||
bool WidgetIsVisible(const WindowBase& w, WidgetIndex widgetIndex);
|
||||
bool WidgetIsPressed(const WindowBase& w, WidgetIndex widgetIndex);
|
||||
bool WidgetIsHighlighted(const WindowBase& w, WidgetIndex widgetIndex);
|
||||
bool WidgetIsActiveTool(const WindowBase& w, WidgetIndex widgetIndex);
|
||||
void WidgetScrollGetPart(
|
||||
WindowBase& w, const Widget* widget, const ScreenCoordsXY& screenCoords, ScreenCoordsXY& retScreenCoords,
|
||||
int32_t* output_scroll_area, int32_t* scroll_id);
|
||||
|
||||
void WidgetSetEnabled(WindowBase& w, WidgetIndex widgetIndex, bool enabled);
|
||||
void WidgetSetDisabled(WindowBase& w, WidgetIndex widgetIndex, bool value);
|
||||
void WidgetSetHoldable(WindowBase& w, WidgetIndex widgetIndex, bool value);
|
||||
void WidgetSetVisible(WindowBase& w, WidgetIndex widgetIndex, bool value);
|
||||
void WidgetSetPressed(WindowBase& w, WidgetIndex widgetIndex, bool value);
|
||||
void WidgetSetCheckboxValue(WindowBase& w, WidgetIndex widgetIndex, bool value);
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include "Window.h"
|
||||
|
||||
#include "Theme.h"
|
||||
#include "Widget.h"
|
||||
#include "openrct2/world/Location.hpp"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <algorithm>
|
||||
|
@ -503,7 +505,7 @@ static bool WindowOtherWheelInput(WindowBase& w, WidgetIndex widgetIndex, int32_
|
|||
return false;
|
||||
}
|
||||
|
||||
WindowEventMouseDownCall(&w, buttonWidgetIndex);
|
||||
w.OnMouseDown(buttonWidgetIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -580,7 +582,6 @@ void WindowInitScrollWidgets(WindowBase& w)
|
|||
{
|
||||
Widget* widget;
|
||||
int32_t widget_index, scroll_index;
|
||||
int32_t width, height;
|
||||
|
||||
widget_index = 0;
|
||||
scroll_index = 0;
|
||||
|
@ -594,13 +595,11 @@ void WindowInitScrollWidgets(WindowBase& w)
|
|||
|
||||
auto& scroll = w.scrolls[scroll_index];
|
||||
scroll.flags = 0;
|
||||
width = 0;
|
||||
height = 0;
|
||||
WindowGetScrollSize(&w, scroll_index, &width, &height);
|
||||
ScreenSize scrollSize = w.OnScrollGetSize(scroll_index);
|
||||
scroll.h_left = 0;
|
||||
scroll.h_right = width + 1;
|
||||
scroll.h_right = scrollSize.width + 1;
|
||||
scroll.v_top = 0;
|
||||
scroll.v_bottom = height + 1;
|
||||
scroll.v_bottom = scrollSize.height + 1;
|
||||
|
||||
if (widget->content & SCROLL_HORIZONTAL)
|
||||
scroll.flags |= HSCROLLBAR_VISIBLE;
|
||||
|
@ -639,10 +638,7 @@ void WindowDrawWidgets(WindowBase& w, DrawPixelInfo& dpi)
|
|||
{
|
||||
if (w.windowPos.y + widget->top < dpi.y + dpi.height && w.windowPos.y + widget->bottom >= dpi.y)
|
||||
{
|
||||
if (w.IsLegacy())
|
||||
WidgetDraw(dpi, w, widgetIndex);
|
||||
else
|
||||
w.OnDrawWidget(widgetIndex, dpi);
|
||||
w.OnDrawWidget(widgetIndex, dpi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -688,15 +684,10 @@ void InvalidateAllWindowsAfterInput()
|
|||
WindowVisitEach([](WindowBase* w) {
|
||||
WindowUpdateScrollWidgets(*w);
|
||||
WindowInvalidatePressedImageButton(*w);
|
||||
WindowEventResizeCall(w);
|
||||
w->OnResize();
|
||||
});
|
||||
}
|
||||
|
||||
bool Window::IsLegacy()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void Window::OnDraw(DrawPixelInfo& dpi)
|
||||
{
|
||||
WindowDrawWidgets(*this, dpi);
|
||||
|
@ -808,7 +799,8 @@ void Window::TextInputOpen(
|
|||
WidgetIndex callWidget, StringId title, StringId description, const Formatter& descriptionArgs, StringId existingText,
|
||||
uintptr_t existingArgs, int32_t maxLength)
|
||||
{
|
||||
WindowTextInputOpen(this, callWidget, title, description, descriptionArgs, existingText, existingArgs, maxLength);
|
||||
OpenRCT2::Ui::Windows::WindowTextInputOpen(
|
||||
this, callWidget, title, description, descriptionArgs, existingText, existingArgs, maxLength);
|
||||
}
|
||||
|
||||
void WindowAlignTabs(WindowBase* w, WidgetIndex start_tab_id, WidgetIndex end_tab_id)
|
||||
|
@ -840,3 +832,31 @@ ScreenCoordsXY WindowGetViewportSoundIconPos(WindowBase& w)
|
|||
const uint8_t buttonOffset = (gConfigInterface.WindowButtonsOnTheLeft) ? CloseButtonWidth + 2 : 0;
|
||||
return w.windowPos + ScreenCoordsXY{ 2 + buttonOffset, 2 };
|
||||
}
|
||||
|
||||
namespace OpenRCT2::Ui::Windows
|
||||
{
|
||||
WindowBase* WindowGetListening()
|
||||
{
|
||||
for (auto it = g_window_list.rbegin(); it != g_window_list.rend(); it++)
|
||||
{
|
||||
auto& w = **it;
|
||||
if (w.flags & WF_DEAD)
|
||||
continue;
|
||||
|
||||
auto viewport = w.viewport;
|
||||
if (viewport != nullptr)
|
||||
{
|
||||
if (viewport->flags & VIEWPORT_FLAG_SOUND_ON)
|
||||
{
|
||||
return &w;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
WindowClass WindowGetClassification(const WindowBase& window)
|
||||
{
|
||||
return window.classification;
|
||||
}
|
||||
} // namespace OpenRCT2::Ui::Windows
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
struct Window : WindowBase
|
||||
{
|
||||
virtual bool IsLegacy() override;
|
||||
virtual void OnDraw(DrawPixelInfo& dpi) override;
|
||||
virtual void OnDrawWidget(WidgetIndex widgetIndex, DrawPixelInfo& dpi) override;
|
||||
|
||||
|
@ -41,3 +40,37 @@ void WindowAllWheelInput();
|
|||
void ApplyScreenSaverLockSetting();
|
||||
void WindowAlignTabs(WindowBase* w, WidgetIndex start_tab_id, WidgetIndex end_tab_id);
|
||||
ScreenCoordsXY WindowGetViewportSoundIconPos(WindowBase& w);
|
||||
|
||||
namespace OpenRCT2::Ui::Windows
|
||||
{
|
||||
void RideConstructionToolupdateEntranceExit(const ScreenCoordsXY& screenCoords);
|
||||
void RideConstructionToolupdateConstruct(const ScreenCoordsXY& screenCoords);
|
||||
void RideConstructionTooldownConstruct(const ScreenCoordsXY& screenCoords);
|
||||
void UpdateGhostTrackAndArrow();
|
||||
void WindowRideConstructionKeyboardShortcutTurnLeft();
|
||||
void WindowRideConstructionKeyboardShortcutTurnRight();
|
||||
void WindowRideConstructionKeyboardShortcutUseTrackDefault();
|
||||
void WindowRideConstructionKeyboardShortcutSlopeDown();
|
||||
void WindowRideConstructionKeyboardShortcutSlopeUp();
|
||||
void WindowRideConstructionKeyboardShortcutChainLiftToggle();
|
||||
void WindowRideConstructionKeyboardShortcutBankLeft();
|
||||
void WindowRideConstructionKeyboardShortcutBankRight();
|
||||
void WindowRideConstructionKeyboardShortcutPreviousTrack();
|
||||
void WindowRideConstructionKeyboardShortcutNextTrack();
|
||||
void WindowRideConstructionKeyboardShortcutBuildCurrent();
|
||||
void WindowRideConstructionKeyboardShortcutDemolishCurrent();
|
||||
|
||||
void WindowFootpathKeyboardShortcutTurnLeft();
|
||||
void WindowFootpathKeyboardShortcutTurnRight();
|
||||
void WindowFootpathKeyboardShortcutSlopeDown();
|
||||
void WindowFootpathKeyboardShortcutSlopeUp();
|
||||
void WindowFootpathKeyboardShortcutBuildCurrent();
|
||||
void WindowFootpathKeyboardShortcutDemolishCurrent();
|
||||
|
||||
void WindowTileInspectorKeyboardShortcutToggleInvisibility();
|
||||
|
||||
extern const StringId ColourSchemeNames[4];
|
||||
|
||||
WindowBase* WindowGetListening();
|
||||
WindowClass WindowGetClassification(const WindowBase& window);
|
||||
} // namespace OpenRCT2::Ui::Windows
|
||||
|
|
|
@ -69,11 +69,13 @@
|
|||
<ClInclude Include="interface\Graph.h" />
|
||||
<ClInclude Include="interface\InGameConsole.h" />
|
||||
<ClInclude Include="interface\LandTool.h" />
|
||||
<ClInclude Include="interface\Objective.h" />
|
||||
<ClInclude Include="interface\Theme.h" />
|
||||
<ClInclude Include="interface\Viewport.h" />
|
||||
<ClInclude Include="interface\Widget.h" />
|
||||
<ClInclude Include="interface\Window.h" />
|
||||
<ClInclude Include="ride\Construction.h" />
|
||||
<ClInclude Include="ride\VehicleSounds.h" />
|
||||
<ClInclude Include="scripting\CustomImages.h" />
|
||||
<ClInclude Include="scripting\CustomListView.h" />
|
||||
<ClInclude Include="scripting\CustomMenu.h" />
|
||||
|
@ -128,11 +130,13 @@
|
|||
<ClCompile Include="interface\Graph.cpp" />
|
||||
<ClCompile Include="interface\InGameConsole.cpp" />
|
||||
<ClCompile Include="interface\LandTool.cpp" />
|
||||
<ClCompile Include="interface\Objective.cpp" />
|
||||
<ClCompile Include="interface\Theme.cpp" />
|
||||
<ClCompile Include="interface\ViewportInteraction.cpp" />
|
||||
<ClCompile Include="interface\Widget.cpp" />
|
||||
<ClCompile Include="interface\Window.cpp" />
|
||||
<ClCompile Include="ride\Construction.cpp" />
|
||||
<ClCompile Include="ride\VehicleSounds.cpp" />
|
||||
<ClCompile Include="scripting\CustomImages.cpp" />
|
||||
<ClCompile Include="scripting\CustomListView.cpp" />
|
||||
<ClCompile Include="scripting\CustomMenu.cpp" />
|
||||
|
@ -246,4 +250,4 @@
|
|||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "Construction.h"
|
||||
|
||||
#include <openrct2/GameState.h>
|
||||
#include <openrct2/actions/RideCreateAction.h>
|
||||
#include <openrct2/ride/Ride.h>
|
||||
#include <openrct2/ride/RideConstruction.h>
|
||||
|
@ -28,7 +29,8 @@ void RideConstructNew(RideSelection listItem)
|
|||
int32_t colour1 = RideGetRandomColourPresetIndex(listItem.Type);
|
||||
int32_t colour2 = RideGetUnusedPresetVehicleColour(rideEntryIndex);
|
||||
|
||||
auto gameAction = RideCreateAction(listItem.Type, listItem.EntryIndex, colour1, colour2, gLastEntranceStyle);
|
||||
auto gameAction = RideCreateAction(
|
||||
listItem.Type, listItem.EntryIndex, colour1, colour2, OpenRCT2::GetGameState().LastEntranceStyle);
|
||||
|
||||
gameAction.SetCallback([](const GameAction* ga, const GameActions::Result* result) {
|
||||
if (result->Error != GameActions::Status::Ok)
|
||||
|
|
|
@ -0,0 +1,605 @@
|
|||
#include "VehicleSounds.h"
|
||||
|
||||
#include "../interface/Viewport.h"
|
||||
#include "../interface/Window.h"
|
||||
|
||||
#include <numeric>
|
||||
#include <openrct2/Context.h>
|
||||
#include <openrct2/GameState.h>
|
||||
#include <openrct2/OpenRCT2.h>
|
||||
#include <openrct2/audio/AudioChannel.h>
|
||||
#include <openrct2/audio/AudioMixer.h>
|
||||
#include <openrct2/audio/audio.h>
|
||||
#include <openrct2/core/FixedVector.h>
|
||||
#include <openrct2/entity/EntityRegistry.h>
|
||||
#include <openrct2/profiling/Profiling.h>
|
||||
#include <openrct2/ride/TrainManager.h>
|
||||
#include <openrct2/ride/Vehicle.h>
|
||||
|
||||
namespace OpenRCT2::Audio
|
||||
{
|
||||
namespace
|
||||
{
|
||||
template<typename T> class TrainIterator;
|
||||
template<typename T> class Train
|
||||
{
|
||||
public:
|
||||
explicit Train(T* vehicle)
|
||||
: FirstCar(vehicle)
|
||||
{
|
||||
assert(FirstCar->IsHead());
|
||||
}
|
||||
int32_t GetMass() const;
|
||||
|
||||
friend class TrainIterator<T>;
|
||||
using iterator = TrainIterator<T>;
|
||||
iterator begin() const
|
||||
{
|
||||
return iterator{ FirstCar };
|
||||
}
|
||||
iterator end() const
|
||||
{
|
||||
return iterator{};
|
||||
}
|
||||
|
||||
private:
|
||||
T* FirstCar;
|
||||
};
|
||||
template<typename T> class TrainIterator
|
||||
{
|
||||
public:
|
||||
using iterator = TrainIterator;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
|
||||
TrainIterator() = default;
|
||||
explicit TrainIterator(T* vehicle)
|
||||
: Current(vehicle)
|
||||
{
|
||||
}
|
||||
reference operator*() const
|
||||
{
|
||||
return *Current;
|
||||
}
|
||||
iterator& operator++()
|
||||
{
|
||||
Current = GetEntity<Vehicle>(NextVehicleId);
|
||||
if (Current != nullptr)
|
||||
{
|
||||
NextVehicleId = Current->next_vehicle_on_train;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
iterator operator++(int)
|
||||
{
|
||||
iterator temp = *this;
|
||||
++*this;
|
||||
return temp;
|
||||
}
|
||||
bool operator!=(const iterator& other) const
|
||||
{
|
||||
return Current != other.Current;
|
||||
}
|
||||
|
||||
private:
|
||||
T* Current = nullptr;
|
||||
EntityId NextVehicleId = EntityId::GetNull();
|
||||
};
|
||||
} // namespace
|
||||
|
||||
template<typename T> int32_t Train<T>::GetMass() const
|
||||
{
|
||||
return std::accumulate(
|
||||
begin(), end(), 0, [](int32_t totalMass, const Vehicle& vehicle) { return totalMass + vehicle.mass; });
|
||||
}
|
||||
|
||||
static bool SoundCanPlay(const Vehicle& vehicle)
|
||||
{
|
||||
if (gScreenFlags & SCREEN_FLAGS_SCENARIO_EDITOR)
|
||||
return false;
|
||||
|
||||
if ((gScreenFlags & SCREEN_FLAGS_TRACK_DESIGNER) && GetGameState().EditorStep != EditorStep::RollercoasterDesigner)
|
||||
return false;
|
||||
|
||||
if (vehicle.sound1_id == SoundId::Null && vehicle.sound2_id == SoundId::Null)
|
||||
return false;
|
||||
|
||||
if (vehicle.x == LOCATION_NULL)
|
||||
return false;
|
||||
|
||||
if (g_music_tracking_viewport == nullptr)
|
||||
return false;
|
||||
|
||||
const auto quarter_w = g_music_tracking_viewport->view_width / 4;
|
||||
const auto quarter_h = g_music_tracking_viewport->view_height / 4;
|
||||
|
||||
auto left = g_music_tracking_viewport->viewPos.x;
|
||||
auto bottom = g_music_tracking_viewport->viewPos.y;
|
||||
|
||||
if (Ui::Windows::WindowGetClassification(*gWindowAudioExclusive) == WindowClass::MainWindow)
|
||||
{
|
||||
left -= quarter_w;
|
||||
bottom -= quarter_h;
|
||||
}
|
||||
|
||||
if (left >= vehicle.SpriteData.SpriteRect.GetRight() || bottom >= vehicle.SpriteData.SpriteRect.GetBottom())
|
||||
return false;
|
||||
|
||||
auto right = g_music_tracking_viewport->view_width + left;
|
||||
auto top = g_music_tracking_viewport->view_height + bottom;
|
||||
|
||||
if (Ui::Windows::WindowGetClassification(*gWindowAudioExclusive) == WindowClass::MainWindow)
|
||||
{
|
||||
right += quarter_w + quarter_w;
|
||||
top += quarter_h + quarter_h;
|
||||
}
|
||||
|
||||
if (right < vehicle.SpriteData.SpriteRect.GetRight() || top < vehicle.SpriteData.SpriteRect.GetTop())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006BC2F3
|
||||
*/
|
||||
static uint16_t GetSoundPriority(const Vehicle& vehicle)
|
||||
{
|
||||
int32_t result = Train(&vehicle).GetMass() + (std::abs(vehicle.velocity) >> 13);
|
||||
|
||||
for (const auto& vehicleSound : gVehicleSoundList)
|
||||
{
|
||||
if (vehicleSound.id == vehicle.Id.ToUnderlying())
|
||||
{
|
||||
// Vehicle sounds will get higher priority if they are already playing
|
||||
return result + 300;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static VehicleSoundParams CreateSoundParam(const Vehicle& vehicle, uint16_t priority)
|
||||
{
|
||||
VehicleSoundParams param;
|
||||
param.priority = priority;
|
||||
int32_t panX = (vehicle.SpriteData.SpriteRect.GetLeft() / 2) + (vehicle.SpriteData.SpriteRect.GetRight() / 2)
|
||||
- g_music_tracking_viewport->viewPos.x;
|
||||
panX = g_music_tracking_viewport->zoom.ApplyInversedTo(panX);
|
||||
panX += g_music_tracking_viewport->pos.x;
|
||||
|
||||
uint16_t screenWidth = ContextGetWidth();
|
||||
if (screenWidth < 64)
|
||||
{
|
||||
screenWidth = 64;
|
||||
}
|
||||
param.pan_x = ((((panX * 65536) / screenWidth) - 0x8000) >> 4);
|
||||
|
||||
int32_t panY = (vehicle.SpriteData.SpriteRect.GetTop() / 2) + (vehicle.SpriteData.SpriteRect.GetBottom() / 2)
|
||||
- g_music_tracking_viewport->viewPos.y;
|
||||
panY = g_music_tracking_viewport->zoom.ApplyInversedTo(panY);
|
||||
panY += g_music_tracking_viewport->pos.y;
|
||||
|
||||
uint16_t screenHeight = ContextGetHeight();
|
||||
if (screenHeight < 64)
|
||||
{
|
||||
screenHeight = 64;
|
||||
}
|
||||
param.pan_y = ((((panY * 65536) / screenHeight) - 0x8000) >> 4);
|
||||
|
||||
int32_t frequency = std::abs(vehicle.velocity);
|
||||
|
||||
const auto* rideType = vehicle.GetRideEntry();
|
||||
if (rideType != nullptr)
|
||||
{
|
||||
if (rideType->Cars[vehicle.vehicle_type].double_sound_frequency & 1)
|
||||
{
|
||||
frequency *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
// * 0.0105133...
|
||||
frequency >>= 5; // /32
|
||||
frequency *= 5512;
|
||||
frequency >>= 14; // /16384
|
||||
|
||||
frequency += 11025;
|
||||
frequency += 16 * vehicle.sound_vector_factor;
|
||||
param.frequency = static_cast<uint16_t>(frequency);
|
||||
param.id = vehicle.Id.ToUnderlying();
|
||||
param.volume = 0;
|
||||
|
||||
if (vehicle.x != LOCATION_NULL)
|
||||
{
|
||||
auto surfaceElement = MapGetSurfaceElementAt(CoordsXY{ vehicle.x, vehicle.y });
|
||||
|
||||
// vehicle underground
|
||||
if (surfaceElement != nullptr && surfaceElement->GetBaseZ() > vehicle.z)
|
||||
{
|
||||
param.volume = 0x30;
|
||||
}
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006BB9FF
|
||||
*/
|
||||
static void UpdateSoundParams(
|
||||
const Vehicle& vehicle, FixedVector<VehicleSoundParams, MaxVehicleSounds>& vehicleSoundParamsList)
|
||||
{
|
||||
if (!SoundCanPlay(vehicle))
|
||||
return;
|
||||
|
||||
uint16_t soundPriority = GetSoundPriority(vehicle);
|
||||
// Find a sound param of lower priority to use
|
||||
auto soundParamIter = std::find_if(
|
||||
vehicleSoundParamsList.begin(), vehicleSoundParamsList.end(),
|
||||
[soundPriority](const auto& param) { return soundPriority > param.priority; });
|
||||
|
||||
if (soundParamIter == std::end(vehicleSoundParamsList))
|
||||
{
|
||||
if (vehicleSoundParamsList.size() < MaxVehicleSounds)
|
||||
{
|
||||
vehicleSoundParamsList.push_back(CreateSoundParam(vehicle, soundPriority));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vehicleSoundParamsList.size() < MaxVehicleSounds)
|
||||
{
|
||||
// Shift all sound params down one if using a free space
|
||||
vehicleSoundParamsList.insert(soundParamIter, CreateSoundParam(vehicle, soundPriority));
|
||||
}
|
||||
else
|
||||
{
|
||||
*soundParamIter = CreateSoundParam(vehicle, soundPriority);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void VehicleSoundsUpdateWindowSetup()
|
||||
{
|
||||
g_music_tracking_viewport = nullptr;
|
||||
|
||||
WindowBase* window = Ui::Windows::WindowGetListening();
|
||||
if (window == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Viewport* viewport = WindowGetViewport(window);
|
||||
if (viewport == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_music_tracking_viewport = viewport;
|
||||
gWindowAudioExclusive = window;
|
||||
if (viewport->zoom <= ZoomLevel{ 0 })
|
||||
gVolumeAdjustZoom = 0;
|
||||
else if (viewport->zoom == ZoomLevel{ 1 })
|
||||
gVolumeAdjustZoom = 35;
|
||||
else
|
||||
gVolumeAdjustZoom = 70;
|
||||
}
|
||||
|
||||
static uint8_t VehicleSoundsUpdateGetPanVolume(VehicleSoundParams* sound_params)
|
||||
{
|
||||
uint8_t vol1 = 0xFF;
|
||||
uint8_t vol2 = 0xFF;
|
||||
|
||||
int16_t pan_y = std::abs(sound_params->pan_y);
|
||||
pan_y = std::min(static_cast<int16_t>(0xFFF), pan_y);
|
||||
pan_y -= 0x800;
|
||||
if (pan_y > 0)
|
||||
{
|
||||
pan_y = (0x400 - pan_y) / 4;
|
||||
vol1 = LoByte(pan_y);
|
||||
if (static_cast<int8_t>(HiByte(pan_y)) != 0)
|
||||
{
|
||||
vol1 = 0xFF;
|
||||
if (static_cast<int8_t>(HiByte(pan_y)) < 0)
|
||||
{
|
||||
vol1 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int16_t pan_x = std::abs(sound_params->pan_x);
|
||||
pan_x = std::min(static_cast<int16_t>(0xFFF), pan_x);
|
||||
pan_x -= 0x800;
|
||||
|
||||
if (pan_x > 0)
|
||||
{
|
||||
pan_x = (0x400 - pan_x) / 4;
|
||||
vol2 = LoByte(pan_x);
|
||||
if (static_cast<int8_t>(HiByte(pan_x)) != 0)
|
||||
{
|
||||
vol2 = 0xFF;
|
||||
if (static_cast<int8_t>(HiByte(pan_x)) < 0)
|
||||
{
|
||||
vol2 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vol1 = std::min(vol1, vol2);
|
||||
return std::max(0, vol1 - gVolumeAdjustZoom);
|
||||
}
|
||||
|
||||
/* Returns the vehicle sound for a sound_param.
|
||||
*
|
||||
* If already playing returns sound.
|
||||
* If not playing allocates a sound slot to sound_param->id.
|
||||
* If no free slots returns nullptr.
|
||||
*/
|
||||
static VehicleSound* VehicleSoundsUpdateGetVehicleSound(VehicleSoundParams* sound_params)
|
||||
{
|
||||
// Search for already playing vehicle sound
|
||||
for (auto& vehicleSound : gVehicleSoundList)
|
||||
{
|
||||
if (vehicleSound.id == sound_params->id)
|
||||
return &vehicleSound;
|
||||
}
|
||||
|
||||
// No sound already playing
|
||||
for (auto& vehicleSound : gVehicleSoundList)
|
||||
{
|
||||
// Use free slot
|
||||
if (vehicleSound.id == SoundIdNull)
|
||||
{
|
||||
vehicleSound.id = sound_params->id;
|
||||
vehicleSound.TrackSound.Id = SoundId::Null;
|
||||
vehicleSound.OtherSound.Id = SoundId::Null;
|
||||
vehicleSound.volume = 0x30;
|
||||
return &vehicleSound;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool IsLoopingSound(SoundId id)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case SoundId::LiftClassic:
|
||||
case SoundId::TrackFrictionClassicWood:
|
||||
case SoundId::FrictionClassic:
|
||||
case SoundId::LiftFrictionWheels:
|
||||
case SoundId::GoKartEngine:
|
||||
case SoundId::TrackFrictionTrain:
|
||||
case SoundId::TrackFrictionWater:
|
||||
case SoundId::LiftArrow:
|
||||
case SoundId::LiftWood:
|
||||
case SoundId::TrackFrictionWood:
|
||||
case SoundId::LiftWildMouse:
|
||||
case SoundId::LiftBM:
|
||||
case SoundId::TrackFrictionBM:
|
||||
case SoundId::LiftRMC:
|
||||
case SoundId::TrackFrictionRMC:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsFixedFrequencySound(SoundId id)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case SoundId::Scream1:
|
||||
case SoundId::Scream2:
|
||||
case SoundId::Scream3:
|
||||
case SoundId::Scream4:
|
||||
case SoundId::Scream5:
|
||||
case SoundId::Scream6:
|
||||
case SoundId::Scream7:
|
||||
case SoundId::Scream8:
|
||||
case SoundId::TrainWhistle:
|
||||
case SoundId::TrainDeparting:
|
||||
case SoundId::Tram:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsSpecialFrequencySound(SoundId id)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case SoundId::TrackFrictionBM:
|
||||
case SoundId::TrackFrictionRMC:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
enum class SoundType
|
||||
{
|
||||
TrackNoises,
|
||||
OtherNoises, // e.g. Screams
|
||||
};
|
||||
|
||||
template<SoundType type> static uint16_t SoundFrequency(const SoundId id, uint16_t baseFrequency)
|
||||
{
|
||||
if constexpr (type == SoundType::TrackNoises)
|
||||
{
|
||||
if (IsSpecialFrequencySound(id))
|
||||
{
|
||||
return (baseFrequency / 2) + 4000;
|
||||
}
|
||||
return baseFrequency;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsFixedFrequencySound(id))
|
||||
{
|
||||
return 22050;
|
||||
}
|
||||
return std::min((baseFrequency * 2) - 3248, 25700);
|
||||
}
|
||||
}
|
||||
|
||||
template<SoundType type> static bool ShouldUpdateChannelRate(const SoundId id)
|
||||
{
|
||||
return type == SoundType::TrackNoises || !IsFixedFrequencySound(id);
|
||||
}
|
||||
|
||||
template<SoundType type>
|
||||
static void UpdateSound(const SoundId id, int32_t volume, VehicleSoundParams* sound_params, Sound& sound, uint8_t panVol)
|
||||
{
|
||||
volume *= panVol;
|
||||
volume = volume / 8;
|
||||
volume = std::max(volume - 0x1FFF, -10000);
|
||||
|
||||
if (sound.Channel != nullptr && sound.Channel->IsDone())
|
||||
{
|
||||
sound.Id = SoundId::Null;
|
||||
sound.Channel = nullptr;
|
||||
}
|
||||
if (id != sound.Id && sound.Id != SoundId::Null)
|
||||
{
|
||||
sound.Id = SoundId::Null;
|
||||
sound.Channel->Stop();
|
||||
}
|
||||
if (id == SoundId::Null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (sound.Id == SoundId::Null)
|
||||
{
|
||||
auto frequency = SoundFrequency<type>(id, sound_params->frequency);
|
||||
auto looping = IsLoopingSound(id);
|
||||
auto pan = sound_params->pan_x;
|
||||
auto channel = CreateAudioChannel(
|
||||
id, looping, DStoMixerVolume(volume), DStoMixerPan(pan), DStoMixerRate(frequency), false);
|
||||
if (channel != nullptr)
|
||||
{
|
||||
sound.Id = id;
|
||||
sound.Pan = sound_params->pan_x;
|
||||
sound.Volume = volume;
|
||||
sound.Frequency = sound_params->frequency;
|
||||
sound.Channel = channel;
|
||||
}
|
||||
else
|
||||
{
|
||||
sound.Id = SoundId::Null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (volume != sound.Volume)
|
||||
{
|
||||
sound.Volume = volume;
|
||||
sound.Channel->SetVolume(DStoMixerVolume(volume));
|
||||
}
|
||||
if (sound_params->pan_x != sound.Pan)
|
||||
{
|
||||
sound.Pan = sound_params->pan_x;
|
||||
sound.Channel->SetPan(DStoMixerPan(sound_params->pan_x));
|
||||
}
|
||||
if (!(GetGameState().CurrentTicks & 3) && sound_params->frequency != sound.Frequency)
|
||||
{
|
||||
sound.Frequency = sound_params->frequency;
|
||||
if (ShouldUpdateChannelRate<type>(id))
|
||||
{
|
||||
uint16_t frequency = SoundFrequency<type>(id, sound_params->frequency);
|
||||
sound.Channel->SetRate(DStoMixerRate(frequency));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006BBC6B
|
||||
*/
|
||||
void UpdateVehicleSounds()
|
||||
{
|
||||
PROFILED_FUNCTION();
|
||||
|
||||
if (!IsAvailable())
|
||||
return;
|
||||
|
||||
FixedVector<VehicleSoundParams, MaxVehicleSounds> vehicleSoundParamsList;
|
||||
|
||||
VehicleSoundsUpdateWindowSetup();
|
||||
|
||||
for (auto vehicle : TrainManager::View())
|
||||
{
|
||||
UpdateSoundParams(*vehicle, vehicleSoundParamsList);
|
||||
}
|
||||
|
||||
// Stop all playing sounds that no longer have priority to play after vehicle_update_sound_params
|
||||
for (auto& vehicleSound : gVehicleSoundList)
|
||||
{
|
||||
if (vehicleSound.id != SoundIdNull)
|
||||
{
|
||||
bool keepPlaying = false;
|
||||
for (auto vehicleSoundParams : vehicleSoundParamsList)
|
||||
{
|
||||
if (vehicleSound.id == vehicleSoundParams.id)
|
||||
{
|
||||
keepPlaying = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (keepPlaying)
|
||||
continue;
|
||||
|
||||
if (vehicleSound.TrackSound.Id != SoundId::Null)
|
||||
{
|
||||
vehicleSound.TrackSound.Channel->Stop();
|
||||
}
|
||||
if (vehicleSound.OtherSound.Id != SoundId::Null)
|
||||
{
|
||||
vehicleSound.OtherSound.Channel->Stop();
|
||||
}
|
||||
vehicleSound.id = SoundIdNull;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& vehicleSoundParams : vehicleSoundParamsList)
|
||||
{
|
||||
uint8_t panVol = VehicleSoundsUpdateGetPanVolume(&vehicleSoundParams);
|
||||
|
||||
auto* vehicleSound = VehicleSoundsUpdateGetVehicleSound(&vehicleSoundParams);
|
||||
// No free vehicle sound slots (RCT2 corrupts the pointer here)
|
||||
if (vehicleSound == nullptr)
|
||||
continue;
|
||||
|
||||
// Move the Sound Volume towards the SoundsParam Volume
|
||||
int32_t tempvolume = vehicleSound->volume;
|
||||
if (tempvolume != vehicleSoundParams.volume)
|
||||
{
|
||||
if (tempvolume < vehicleSoundParams.volume)
|
||||
{
|
||||
tempvolume += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
tempvolume -= 4;
|
||||
}
|
||||
}
|
||||
vehicleSound->volume = tempvolume;
|
||||
panVol = std::max(0, panVol - tempvolume);
|
||||
|
||||
Vehicle* vehicle = GetEntity<Vehicle>(EntityId::FromUnderlying(vehicleSoundParams.id));
|
||||
if (vehicle != nullptr)
|
||||
{
|
||||
UpdateSound<SoundType::TrackNoises>(
|
||||
vehicle->sound1_id, vehicle->sound1_volume, &vehicleSoundParams, vehicleSound->TrackSound, panVol);
|
||||
UpdateSound<SoundType::OtherNoises>(
|
||||
vehicle->sound2_id, vehicle->sound2_volume, &vehicleSoundParams, vehicleSound->OtherSound, panVol);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace OpenRCT2::Audio
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
namespace OpenRCT2::Audio
|
||||
{
|
||||
void UpdateVehicleSounds();
|
||||
}
|
|
@ -275,6 +275,21 @@ namespace OpenRCT2::Scripting
|
|||
return unpadded;
|
||||
}
|
||||
|
||||
static ImportMode getImportModeFromPalette(const PixelDataPaletteKind& palette)
|
||||
{
|
||||
switch (palette)
|
||||
{
|
||||
case PixelDataPaletteKind::Closest:
|
||||
return ImportMode::Closest;
|
||||
case PixelDataPaletteKind::Dither:
|
||||
return ImportMode::Dithering;
|
||||
case PixelDataPaletteKind::None:
|
||||
case PixelDataPaletteKind::Keep:
|
||||
default:
|
||||
return ImportMode::Default;
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> GetBufferFromPixelData(duk_context* ctx, PixelData& pixelData)
|
||||
{
|
||||
std::vector<uint8_t> imageData;
|
||||
|
@ -303,18 +318,14 @@ namespace OpenRCT2::Scripting
|
|||
case PixelDataKind::Png:
|
||||
{
|
||||
auto imageFormat = pixelData.Palette == PixelDataPaletteKind::Keep ? IMAGE_FORMAT::PNG : IMAGE_FORMAT::PNG_32;
|
||||
auto palette = pixelData.Palette == PixelDataPaletteKind::Keep ? ImageImporter::Palette::KeepIndices
|
||||
: ImageImporter::Palette::OpenRCT2;
|
||||
auto importMode = ImageImporter::ImportMode::Default;
|
||||
if (pixelData.Palette == PixelDataPaletteKind::Closest)
|
||||
importMode = ImageImporter::ImportMode::Closest;
|
||||
else if (pixelData.Palette == PixelDataPaletteKind::Dither)
|
||||
importMode = ImageImporter::ImportMode::Dithering;
|
||||
auto palette = pixelData.Palette == PixelDataPaletteKind::Keep ? Palette::KeepIndices : Palette::OpenRCT2;
|
||||
auto importMode = getImportModeFromPalette(pixelData.Palette);
|
||||
auto pngData = DukGetDataFromBufferLikeObject(pixelData.Data);
|
||||
auto image = Imaging::ReadFromBuffer(pngData, imageFormat);
|
||||
ImageImportMeta meta = { { 0, 0 }, palette, ImportFlags::RLE, importMode };
|
||||
|
||||
ImageImporter importer;
|
||||
auto importResult = importer.Import(image, 0, 0, palette, ImageImporter::ImportFlags::RLE, importMode);
|
||||
auto importResult = importer.Import(image, meta);
|
||||
|
||||
pixelData.Type = PixelDataKind::Rle;
|
||||
pixelData.Width = importResult.Element.width;
|
||||
|
|
|
@ -508,11 +508,11 @@ namespace OpenRCT2::Ui::Windows
|
|||
if (listView.GetScrollbars() == ScrollbarType::Horizontal
|
||||
|| listView.GetScrollbars() == ScrollbarType::Both)
|
||||
{
|
||||
wheight -= SCROLLBAR_WIDTH + 1;
|
||||
wheight -= kScrollBarWidth + 1;
|
||||
}
|
||||
if (listView.GetScrollbars() == ScrollbarType::Vertical || listView.GetScrollbars() == ScrollbarType::Both)
|
||||
{
|
||||
wwidth -= SCROLLBAR_WIDTH + 1;
|
||||
wwidth -= kScrollBarWidth + 1;
|
||||
}
|
||||
listView.Resize({ wwidth, wheight });
|
||||
scrollIndex++;
|
||||
|
@ -745,8 +745,8 @@ namespace OpenRCT2::Ui::Windows
|
|||
RefreshWidgets();
|
||||
|
||||
Invalidate();
|
||||
WindowEventResizeCall(this);
|
||||
WindowEventOnPrepareDrawCall(this);
|
||||
OnResize();
|
||||
OnPrepareDraw();
|
||||
WindowInitScrollWidgets(*this);
|
||||
Invalidate();
|
||||
|
||||
|
@ -931,7 +931,7 @@ namespace OpenRCT2::Ui::Windows
|
|||
}
|
||||
}
|
||||
|
||||
widgetList.push_back(WIDGETS_END);
|
||||
widgetList.push_back(kWidgetsEnd);
|
||||
widgets = widgetList.data();
|
||||
|
||||
WindowInitScrollWidgets(*this);
|
||||
|
|
|
@ -175,7 +175,7 @@ namespace OpenRCT2::Scripting
|
|||
|
||||
void clear()
|
||||
{
|
||||
GfxClear(&_dpi, _fill);
|
||||
GfxClear(_dpi, _fill);
|
||||
}
|
||||
|
||||
void clip(int32_t x, int32_t y, int32_t width, int32_t height)
|
||||
|
|
|
@ -231,7 +231,7 @@ namespace OpenRCT2::Scripting
|
|||
|
||||
void showError(const std::string& title, const std::string& message)
|
||||
{
|
||||
WindowErrorOpen(title, message);
|
||||
ErrorOpen(title, message);
|
||||
}
|
||||
|
||||
void showTextInput(const DukValue& desc)
|
||||
|
@ -282,7 +282,7 @@ namespace OpenRCT2::Scripting
|
|||
else
|
||||
throw DukException();
|
||||
|
||||
WindowLoadsaveOpen(
|
||||
LoadsaveOpen(
|
||||
loadSaveType, defaultPath,
|
||||
[this, plugin, callback](int32_t result, std::string_view path) {
|
||||
if (result == MODAL_RESULT_OK)
|
||||
|
@ -304,7 +304,7 @@ namespace OpenRCT2::Scripting
|
|||
auto plugin = _scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||
auto callback = desc["callback"];
|
||||
|
||||
WindowScenarioselectOpen([this, plugin, callback](std::string_view path) {
|
||||
ScenarioselectOpen([this, plugin, callback](std::string_view path) {
|
||||
auto dukValue = GetScenarioFile(path);
|
||||
_scriptEngine.ExecutePluginCall(plugin, callback, { dukValue }, false);
|
||||
});
|
||||
|
|
|
@ -111,7 +111,12 @@ namespace OpenRCT2::Scripting
|
|||
|
||||
int32_t rotation_get() const
|
||||
{
|
||||
return GetCurrentRotation();
|
||||
auto viewport = GetViewport();
|
||||
if (viewport != nullptr)
|
||||
{
|
||||
return viewport->rotation;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void rotation_set(int32_t value)
|
||||
{
|
||||
|
@ -120,9 +125,9 @@ namespace OpenRCT2::Scripting
|
|||
auto w = GetWindow();
|
||||
if (w != nullptr)
|
||||
{
|
||||
while (GetCurrentRotation() != value)
|
||||
while (w->viewport->rotation != value)
|
||||
{
|
||||
WindowRotateCamera(*w, 1);
|
||||
ViewportRotateSingle(w, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -179,7 +184,7 @@ namespace OpenRCT2::Scripting
|
|||
if (viewport != nullptr)
|
||||
{
|
||||
auto centre = viewport->viewPos + ScreenCoordsXY{ viewport->view_width / 2, viewport->view_height / 2 };
|
||||
auto coords = ViewportPosToMapPos(centre, 24);
|
||||
auto coords = ViewportPosToMapPos(centre, 24, viewport->rotation);
|
||||
|
||||
auto ctx = GetContext()->GetScriptEngine().GetContext();
|
||||
auto obj = duk_push_object(ctx);
|
||||
|
@ -203,7 +208,7 @@ namespace OpenRCT2::Scripting
|
|||
auto coords = GetCoordsFromObject(position);
|
||||
if (coords)
|
||||
{
|
||||
auto screenCoords = Translate3DTo2DWithZ(GetCurrentRotation(), *coords);
|
||||
auto screenCoords = Translate3DTo2DWithZ(viewport->rotation, *coords);
|
||||
auto left = screenCoords.x - (viewport->view_width / 2);
|
||||
auto top = screenCoords.y - (viewport->view_height / 2);
|
||||
SetViewLeftTop(left, top);
|
||||
|
|
|
@ -19,14 +19,14 @@
|
|||
#include <openrct2/sprites.h>
|
||||
#include <openrct2/ui/UiContext.h>
|
||||
|
||||
using namespace OpenRCT2;
|
||||
namespace OpenRCT2::Ui::Windows
|
||||
{
|
||||
static constexpr int32_t WW = 400;
|
||||
static constexpr int32_t WH = 450;
|
||||
static constexpr StringId WINDOW_TITLE = STR_ABOUT;
|
||||
static constexpr int32_t TABHEIGHT = 50;
|
||||
|
||||
static constexpr int32_t WW = 400;
|
||||
static constexpr int32_t WH = 450;
|
||||
static constexpr StringId WINDOW_TITLE = STR_ABOUT;
|
||||
static constexpr int32_t TABHEIGHT = 50;
|
||||
|
||||
// clang-format off
|
||||
// clang-format off
|
||||
enum
|
||||
{
|
||||
WINDOW_ABOUT_PAGE_OPENRCT2,
|
||||
|
@ -70,12 +70,12 @@ static Widget _windowAboutOpenRCT2Widgets[] = {
|
|||
MakeWidget({168, 115 + 40}, {200, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_CHANGELOG_ELLIPSIS), // changelog button
|
||||
MakeWidget({168, 115 + 60}, {200, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_JOIN_DISCORD ), // "join discord" button
|
||||
MakeWidget({168, 115 + 80}, {200, 14}, WindowWidgetType::Button, WindowColour::Secondary, STR_CONTRIBUTORS_WINDOW_BUTTON), // "contributors" button
|
||||
WIDGETS_END,
|
||||
kWidgetsEnd,
|
||||
};
|
||||
|
||||
static Widget _windowAboutRCT2Widgets[] = {
|
||||
WIDGETS_MAIN,
|
||||
WIDGETS_END,
|
||||
kWidgetsEnd,
|
||||
};
|
||||
|
||||
static Widget *_windowAboutPageWidgets[] = {
|
||||
|
@ -83,205 +83,207 @@ static Widget *_windowAboutPageWidgets[] = {
|
|||
_windowAboutRCT2Widgets,
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
// clang-format on
|
||||
|
||||
class AboutWindow final : public Window
|
||||
{
|
||||
public:
|
||||
void OnOpen() override
|
||||
class AboutWindow final : public Window
|
||||
{
|
||||
widgets = _windowAboutOpenRCT2Widgets;
|
||||
|
||||
WindowInitScrollWidgets(*this);
|
||||
SetPage(WINDOW_ABOUT_PAGE_OPENRCT2);
|
||||
}
|
||||
|
||||
void OnMouseUp(WidgetIndex widgetIndex) override
|
||||
{
|
||||
switch (widgetIndex)
|
||||
public:
|
||||
void OnOpen() override
|
||||
{
|
||||
case WIDX_CLOSE:
|
||||
Close();
|
||||
break;
|
||||
case WIDX_TAB_ABOUT_OPENRCT2:
|
||||
case WIDX_TAB_ABOUT_RCT2:
|
||||
SetPage(widgetIndex - WIDX_TAB_ABOUT_OPENRCT2);
|
||||
break;
|
||||
case WIDX_JOIN_DISCORD:
|
||||
OpenRCT2::GetContext()->GetUiContext()->OpenURL("https://discord.gg/ZXZd8D8");
|
||||
break;
|
||||
case WIDX_CHANGELOG:
|
||||
ContextOpenWindow(WindowClass::Changelog);
|
||||
break;
|
||||
case WIDX_NEW_VERSION:
|
||||
ContextOpenWindowView(WV_NEW_VERSION_INFO);
|
||||
break;
|
||||
case WIDX_COPY_BUILD_INFO:
|
||||
SDL_SetClipboardText(gVersionInfoFull);
|
||||
break;
|
||||
case WIDX_CONTRIBUTORS_BUTTON:
|
||||
ContextOpenWindowView(WV_CONTRIBUTORS);
|
||||
break;
|
||||
widgets = _windowAboutOpenRCT2Widgets;
|
||||
|
||||
WindowInitScrollWidgets(*this);
|
||||
SetPage(WINDOW_ABOUT_PAGE_OPENRCT2);
|
||||
}
|
||||
}
|
||||
|
||||
void OnDraw(DrawPixelInfo& dpi) override
|
||||
{
|
||||
DrawWidgets(dpi);
|
||||
|
||||
const auto& aboutOpenRCT2 = widgets[WIDX_TAB_ABOUT_OPENRCT2];
|
||||
const auto& aboutRCT2 = widgets[WIDX_TAB_ABOUT_RCT2];
|
||||
|
||||
int32_t y = windowPos.y + aboutOpenRCT2.midY() - 3;
|
||||
ScreenCoordsXY aboutOpenRCT2Coords(windowPos.x + aboutOpenRCT2.left + 45, y);
|
||||
ScreenCoordsXY aboutRCT2Coords(windowPos.x + aboutRCT2.left + 45, y);
|
||||
|
||||
// Draw tab names
|
||||
void OnMouseUp(WidgetIndex widgetIndex) override
|
||||
{
|
||||
switch (widgetIndex)
|
||||
{
|
||||
case WIDX_CLOSE:
|
||||
Close();
|
||||
break;
|
||||
case WIDX_TAB_ABOUT_OPENRCT2:
|
||||
case WIDX_TAB_ABOUT_RCT2:
|
||||
SetPage(widgetIndex - WIDX_TAB_ABOUT_OPENRCT2);
|
||||
break;
|
||||
case WIDX_JOIN_DISCORD:
|
||||
OpenRCT2::GetContext()->GetUiContext()->OpenURL("https://discord.gg/ZXZd8D8");
|
||||
break;
|
||||
case WIDX_CHANGELOG:
|
||||
ContextOpenWindow(WindowClass::Changelog);
|
||||
break;
|
||||
case WIDX_NEW_VERSION:
|
||||
ContextOpenWindowView(WV_NEW_VERSION_INFO);
|
||||
break;
|
||||
case WIDX_COPY_BUILD_INFO:
|
||||
SDL_SetClipboardText(gVersionInfoFull);
|
||||
break;
|
||||
case WIDX_CONTRIBUTORS_BUTTON:
|
||||
ContextOpenWindowView(WV_CONTRIBUTORS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnDraw(DrawPixelInfo& dpi) override
|
||||
{
|
||||
DrawWidgets(dpi);
|
||||
|
||||
const auto& aboutOpenRCT2 = widgets[WIDX_TAB_ABOUT_OPENRCT2];
|
||||
const auto& aboutRCT2 = widgets[WIDX_TAB_ABOUT_RCT2];
|
||||
|
||||
int32_t y = windowPos.y + aboutOpenRCT2.midY() - 3;
|
||||
ScreenCoordsXY aboutOpenRCT2Coords(windowPos.x + aboutOpenRCT2.left + 45, y);
|
||||
ScreenCoordsXY aboutRCT2Coords(windowPos.x + aboutRCT2.left + 45, y);
|
||||
|
||||
// Draw tab names
|
||||
{
|
||||
auto ft = Formatter();
|
||||
ft.Add<StringId>(STR_TITLE_SEQUENCE_OPENRCT2);
|
||||
DrawTextWrapped(
|
||||
dpi, aboutOpenRCT2Coords, 87, STR_WINDOW_COLOUR_2_STRINGID, ft,
|
||||
{ COLOUR_AQUAMARINE, TextAlignment::CENTRE });
|
||||
}
|
||||
{
|
||||
auto ft = Formatter();
|
||||
ft.Add<StringId>(STR_TITLE_SEQUENCE_RCT2);
|
||||
DrawTextWrapped(
|
||||
dpi, aboutRCT2Coords, 87, STR_WINDOW_COLOUR_2_STRINGID, ft, { COLOUR_AQUAMARINE, TextAlignment::CENTRE });
|
||||
}
|
||||
|
||||
if (page == WINDOW_ABOUT_PAGE_OPENRCT2)
|
||||
{
|
||||
DrawOpenRCT2Info(dpi);
|
||||
}
|
||||
else if (page == WINDOW_ABOUT_PAGE_RCT2)
|
||||
{
|
||||
DrawRCT2Info(dpi);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Set which tab to show
|
||||
*/
|
||||
void SetPage(int32_t p)
|
||||
{
|
||||
page = p;
|
||||
frame_no = 0;
|
||||
pressed_widgets = 0;
|
||||
widgets = _windowAboutPageWidgets[p];
|
||||
|
||||
switch (p)
|
||||
{
|
||||
case WINDOW_ABOUT_PAGE_OPENRCT2:
|
||||
pressed_widgets |= (1uLL << WIDX_TAB_ABOUT_OPENRCT2);
|
||||
break;
|
||||
case WINDOW_ABOUT_PAGE_RCT2:
|
||||
pressed_widgets |= (1uLL << WIDX_TAB_ABOUT_RCT2);
|
||||
break;
|
||||
}
|
||||
|
||||
WindowInitScrollWidgets(*this);
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Draw OpenRCT2 info on open tab
|
||||
*/
|
||||
void DrawOpenRCT2Info(DrawPixelInfo& dpi)
|
||||
{
|
||||
// Draw logo on placeholder widget
|
||||
ScreenCoordsXY logoCoords = windowPos
|
||||
+ ScreenCoordsXY(widgets[WIDX_OPENRCT2_LOGO].left, widgets[WIDX_OPENRCT2_LOGO].top);
|
||||
GfxDrawSprite(dpi, ImageId(SPR_G2_LOGO), logoCoords);
|
||||
// Version info
|
||||
utf8 buffer[256];
|
||||
utf8* ch = buffer;
|
||||
OpenRCT2WriteFullVersionInfo(ch, sizeof(buffer) - (ch - buffer));
|
||||
auto ft = Formatter();
|
||||
ft.Add<StringId>(STR_TITLE_SEQUENCE_OPENRCT2);
|
||||
DrawTextWrapped(
|
||||
dpi, aboutOpenRCT2Coords, 87, STR_WINDOW_COLOUR_2_STRINGID, ft, { COLOUR_AQUAMARINE, TextAlignment::CENTRE });
|
||||
}
|
||||
{
|
||||
auto ft = Formatter();
|
||||
ft.Add<StringId>(STR_TITLE_SEQUENCE_RCT2);
|
||||
DrawTextWrapped(
|
||||
dpi, aboutRCT2Coords, 87, STR_WINDOW_COLOUR_2_STRINGID, ft, { COLOUR_AQUAMARINE, TextAlignment::CENTRE });
|
||||
ft.Add<const char*>(buffer);
|
||||
|
||||
auto const& versionPlaceholder = widgets[WIDX_VERSION];
|
||||
auto versionPlaceHolderWidth = versionPlaceholder.right - versionPlaceholder.left;
|
||||
auto centreX = versionPlaceholder.left + versionPlaceHolderWidth / 2;
|
||||
auto centreY = (versionPlaceholder.top + versionPlaceholder.bottom - FontGetLineHeight(FontStyle::Medium)) / 2;
|
||||
auto centrePos = windowPos + ScreenCoordsXY(centreX, centreY);
|
||||
DrawTextWrapped(dpi, centrePos, versionPlaceHolderWidth, STR_STRING, ft, { colours[1], TextAlignment::CENTRE });
|
||||
|
||||
// Shows the update available button
|
||||
if (OpenRCT2::GetContext()->HasNewVersionInfo())
|
||||
{
|
||||
widgets[WIDX_NEW_VERSION].type = WindowWidgetType::Button;
|
||||
_windowAboutOpenRCT2Widgets[WIDX_NEW_VERSION].type = WindowWidgetType::Button;
|
||||
}
|
||||
|
||||
// Draw the rest of the text
|
||||
Formatter ft2{};
|
||||
TextPaint tp{ colours[1], TextAlignment::CENTRE };
|
||||
auto textCoords = windowPos + ScreenCoordsXY((width / 2) - 1, 240);
|
||||
auto textWidth = WW - 20;
|
||||
|
||||
textCoords += ScreenCoordsXY(
|
||||
0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_OPENRCT2_DESCRIPTION_2, ft2, tp) + 5); // More info
|
||||
textCoords += ScreenCoordsXY(
|
||||
0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_OPENRCT2_DESCRIPTION_3, ft2, tp) + 5); // Copyright
|
||||
textCoords += ScreenCoordsXY(
|
||||
0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_OPENRCT2_TITLE, ft2, tp) + 5); // Title Theme
|
||||
textCoords += ScreenCoordsXY(
|
||||
0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_FAIRGROUND_ORGAN, ft2, tp) + 5); // Fairground organ
|
||||
textCoords += ScreenCoordsXY(
|
||||
0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_SPECIAL_THANKS_1, ft2, tp)); // Special Thanks
|
||||
textCoords += ScreenCoordsXY(
|
||||
0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_SPECIAL_THANKS_2, ft2, tp)); // Company names
|
||||
}
|
||||
|
||||
if (page == WINDOW_ABOUT_PAGE_OPENRCT2)
|
||||
/**
|
||||
* @brief Draw RCT2 info on open tab
|
||||
*/
|
||||
void DrawRCT2Info(DrawPixelInfo& dpi)
|
||||
{
|
||||
DrawOpenRCT2Info(dpi);
|
||||
}
|
||||
else if (page == WINDOW_ABOUT_PAGE_RCT2)
|
||||
{
|
||||
DrawRCT2Info(dpi);
|
||||
}
|
||||
}
|
||||
int32_t yPage = windowPos.y + widgets[WIDX_PAGE_BACKGROUND].top + 5;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Set which tab to show
|
||||
*/
|
||||
void SetPage(int32_t p)
|
||||
{
|
||||
page = p;
|
||||
frame_no = 0;
|
||||
pressed_widgets = 0;
|
||||
widgets = _windowAboutPageWidgets[p];
|
||||
auto screenCoords = ScreenCoordsXY{ windowPos.x + 200, yPage + 5 };
|
||||
|
||||
switch (p)
|
||||
{
|
||||
case WINDOW_ABOUT_PAGE_OPENRCT2:
|
||||
pressed_widgets |= (1uLL << WIDX_TAB_ABOUT_OPENRCT2);
|
||||
break;
|
||||
case WINDOW_ABOUT_PAGE_RCT2:
|
||||
pressed_widgets |= (1uLL << WIDX_TAB_ABOUT_RCT2);
|
||||
break;
|
||||
int32_t lineHeight = FontGetLineHeight(FontStyle::Medium);
|
||||
|
||||
// Credits
|
||||
DrawTextBasic(dpi, screenCoords, STR_COPYRIGHT_CS, {}, { TextAlignment::CENTRE });
|
||||
screenCoords.y += lineHeight + 74;
|
||||
DrawTextBasic(dpi, screenCoords, STR_DESIGNED_AND_PROGRAMMED_BY_CS, {}, { TextAlignment::CENTRE });
|
||||
screenCoords.y += lineHeight;
|
||||
DrawTextBasic(dpi, screenCoords, STR_GRAPHICS_BY_SF, {}, { TextAlignment::CENTRE });
|
||||
screenCoords.y += lineHeight;
|
||||
DrawTextBasic(dpi, screenCoords, STR_SOUND_AND_MUSIC_BY_AB, {}, { TextAlignment::CENTRE });
|
||||
screenCoords.y += lineHeight;
|
||||
DrawTextBasic(dpi, screenCoords, STR_ADDITIONAL_SOUNDS_RECORDED_BY_DE, {}, { TextAlignment::CENTRE });
|
||||
screenCoords.y += lineHeight + 3;
|
||||
DrawTextBasic(dpi, screenCoords, STR_REPRESENTATION_BY_JL, {}, { TextAlignment::CENTRE });
|
||||
screenCoords.y += 2 * lineHeight + 5;
|
||||
DrawTextBasic(dpi, screenCoords, STR_THANKS_TO, {}, { TextAlignment::CENTRE });
|
||||
screenCoords.y += lineHeight;
|
||||
DrawTextBasic(dpi, screenCoords, STR_THANKS_TO_PEOPLE, {}, { TextAlignment::CENTRE });
|
||||
screenCoords.y += 2 * lineHeight + 5;
|
||||
DrawTextBasic(dpi, screenCoords, STR_LICENSED_TO_INFOGRAMES_INTERACTIVE_INC, {}, { TextAlignment::CENTRE });
|
||||
|
||||
// Images
|
||||
GfxDrawSprite(dpi, ImageId(SPR_CREDITS_CHRIS_SAWYER_SMALL), { windowPos.x + 92, yPage + 24 });
|
||||
|
||||
// Licence
|
||||
}
|
||||
|
||||
WindowInitScrollWidgets(*this);
|
||||
Invalidate();
|
||||
}
|
||||
void OnResize() override
|
||||
{
|
||||
ResizeFrameWithPage();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Draw OpenRCT2 info on open tab
|
||||
*
|
||||
* rct2: 0x0066D2AC
|
||||
*/
|
||||
void DrawOpenRCT2Info(DrawPixelInfo& dpi)
|
||||
WindowBase* AboutOpen()
|
||||
{
|
||||
// Draw logo on placeholder widget
|
||||
ScreenCoordsXY logoCoords = windowPos
|
||||
+ ScreenCoordsXY(widgets[WIDX_OPENRCT2_LOGO].left, widgets[WIDX_OPENRCT2_LOGO].top);
|
||||
GfxDrawSprite(dpi, ImageId(SPR_G2_LOGO), logoCoords);
|
||||
// Version info
|
||||
utf8 buffer[256];
|
||||
utf8* ch = buffer;
|
||||
OpenRCT2WriteFullVersionInfo(ch, sizeof(buffer) - (ch - buffer));
|
||||
auto ft = Formatter();
|
||||
ft.Add<const char*>(buffer);
|
||||
|
||||
auto const& versionPlaceholder = widgets[WIDX_VERSION];
|
||||
auto versionPlaceHolderWidth = versionPlaceholder.right - versionPlaceholder.left;
|
||||
auto centreX = versionPlaceholder.left + versionPlaceHolderWidth / 2;
|
||||
auto centreY = (versionPlaceholder.top + versionPlaceholder.bottom - FontGetLineHeight(FontStyle::Medium)) / 2;
|
||||
auto centrePos = windowPos + ScreenCoordsXY(centreX, centreY);
|
||||
DrawTextWrapped(dpi, centrePos, versionPlaceHolderWidth, STR_STRING, ft, { colours[1], TextAlignment::CENTRE });
|
||||
|
||||
// Shows the update available button
|
||||
if (OpenRCT2::GetContext()->HasNewVersionInfo())
|
||||
{
|
||||
widgets[WIDX_NEW_VERSION].type = WindowWidgetType::Button;
|
||||
_windowAboutOpenRCT2Widgets[WIDX_NEW_VERSION].type = WindowWidgetType::Button;
|
||||
}
|
||||
|
||||
// Draw the rest of the text
|
||||
Formatter ft2{};
|
||||
TextPaint tp{ colours[1], TextAlignment::CENTRE };
|
||||
auto textCoords = windowPos + ScreenCoordsXY((width / 2) - 1, 240);
|
||||
auto textWidth = WW - 20;
|
||||
|
||||
textCoords += ScreenCoordsXY(
|
||||
0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_OPENRCT2_DESCRIPTION_2, ft2, tp) + 5); // More info
|
||||
textCoords += ScreenCoordsXY(
|
||||
0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_OPENRCT2_DESCRIPTION_3, ft2, tp) + 5); // Copyright
|
||||
textCoords += ScreenCoordsXY(
|
||||
0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_OPENRCT2_TITLE, ft2, tp) + 5); // Title Theme
|
||||
textCoords += ScreenCoordsXY(
|
||||
0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_FAIRGROUND_ORGAN, ft2, tp) + 5); // Fairground organ
|
||||
textCoords += ScreenCoordsXY(
|
||||
0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_SPECIAL_THANKS_1, ft2, tp)); // Special Thanks
|
||||
textCoords += ScreenCoordsXY(
|
||||
0, DrawTextWrapped(dpi, textCoords, textWidth, STR_ABOUT_SPECIAL_THANKS_2, ft2, tp)); // Company names
|
||||
return WindowFocusOrCreate<AboutWindow>(WindowClass::About, WW, WH, WF_CENTRE_SCREEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Draw RCT2 info on open tab
|
||||
*/
|
||||
void DrawRCT2Info(DrawPixelInfo& dpi)
|
||||
{
|
||||
int32_t yPage = windowPos.y + widgets[WIDX_PAGE_BACKGROUND].top + 5;
|
||||
|
||||
auto screenCoords = ScreenCoordsXY{ windowPos.x + 200, yPage + 5 };
|
||||
|
||||
int32_t lineHeight = FontGetLineHeight(FontStyle::Medium);
|
||||
|
||||
// Credits
|
||||
DrawTextBasic(dpi, screenCoords, STR_COPYRIGHT_CS, {}, { TextAlignment::CENTRE });
|
||||
screenCoords.y += lineHeight + 74;
|
||||
DrawTextBasic(dpi, screenCoords, STR_DESIGNED_AND_PROGRAMMED_BY_CS, {}, { TextAlignment::CENTRE });
|
||||
screenCoords.y += lineHeight;
|
||||
DrawTextBasic(dpi, screenCoords, STR_GRAPHICS_BY_SF, {}, { TextAlignment::CENTRE });
|
||||
screenCoords.y += lineHeight;
|
||||
DrawTextBasic(dpi, screenCoords, STR_SOUND_AND_MUSIC_BY_AB, {}, { TextAlignment::CENTRE });
|
||||
screenCoords.y += lineHeight;
|
||||
DrawTextBasic(dpi, screenCoords, STR_ADDITIONAL_SOUNDS_RECORDED_BY_DE, {}, { TextAlignment::CENTRE });
|
||||
screenCoords.y += lineHeight + 3;
|
||||
DrawTextBasic(dpi, screenCoords, STR_REPRESENTATION_BY_JL, {}, { TextAlignment::CENTRE });
|
||||
screenCoords.y += 2 * lineHeight + 5;
|
||||
DrawTextBasic(dpi, screenCoords, STR_THANKS_TO, {}, { TextAlignment::CENTRE });
|
||||
screenCoords.y += lineHeight;
|
||||
DrawTextBasic(dpi, screenCoords, STR_THANKS_TO_PEOPLE, {}, { TextAlignment::CENTRE });
|
||||
screenCoords.y += 2 * lineHeight + 5;
|
||||
DrawTextBasic(dpi, screenCoords, STR_LICENSED_TO_INFOGRAMES_INTERACTIVE_INC, {}, { TextAlignment::CENTRE });
|
||||
|
||||
// Images
|
||||
GfxDrawSprite(dpi, ImageId(SPR_CREDITS_CHRIS_SAWYER_SMALL), { windowPos.x + 92, yPage + 24 });
|
||||
|
||||
// Licence
|
||||
}
|
||||
|
||||
void OnResize() override
|
||||
{
|
||||
ResizeFrameWithPage();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x0066D2AC
|
||||
*/
|
||||
WindowBase* WindowAboutOpen()
|
||||
{
|
||||
return WindowFocusOrCreate<AboutWindow>(WindowClass::About, WW, WH, WF_CENTRE_SCREEN);
|
||||
}
|
||||
} // namespace OpenRCT2::Ui::Windows
|
||||
|
|
|
@ -18,13 +18,13 @@
|
|||
#include <openrct2/object/ObjectManager.h>
|
||||
#include <openrct2/sprites.h>
|
||||
|
||||
using namespace OpenRCT2;
|
||||
namespace OpenRCT2::Ui::Windows
|
||||
{
|
||||
static constexpr StringId WINDOW_TITLE = STR_ASSET_PACKS;
|
||||
static constexpr int32_t WW = 400;
|
||||
static constexpr int32_t WH = 200;
|
||||
|
||||
static constexpr StringId WINDOW_TITLE = STR_ASSET_PACKS;
|
||||
static constexpr int32_t WW = 400;
|
||||
static constexpr int32_t WH = 200;
|
||||
|
||||
// clang-format off
|
||||
// clang-format off
|
||||
enum WindowAssetPacksWidgetIdx {
|
||||
WIDX_BACKGROUND,
|
||||
WIDX_TITLE,
|
||||
|
@ -45,300 +45,303 @@ static Widget WindowAssetPacksWidgets[] = {
|
|||
MakeWidget({ 0, 0 }, { 0, 0 }, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_G2_ARROW_UP), STR_INCREASE_PRIOTITY_TIP),
|
||||
MakeWidget({ 0, 0 }, { 0, 0 }, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_G2_ARROW_DOWN), STR_DECREASE_PRIOTITY_TIP),
|
||||
MakeWidget({ 0, 0 }, { 0, 0 }, WindowWidgetType::FlatBtn, WindowColour::Secondary, ImageId(SPR_G2_RELOAD), STR_RELOAD_ASSET_PACKS_TIP),
|
||||
WIDGETS_END,
|
||||
kWidgetsEnd,
|
||||
};
|
||||
// clang-format on
|
||||
// clang-format on
|
||||
|
||||
class AssetPacksWindow final : public Window
|
||||
{
|
||||
private:
|
||||
static constexpr int32_t ItemHeight = SCROLLABLE_ROW_HEIGHT + 1;
|
||||
static constexpr int32_t ItemCheckBoxSize = ItemHeight - 3;
|
||||
std::optional<size_t> _highlightedIndex;
|
||||
std::optional<size_t> _selectedIndex;
|
||||
|
||||
public:
|
||||
void OnOpen() override
|
||||
class AssetPacksWindow final : public Window
|
||||
{
|
||||
widgets = WindowAssetPacksWidgets;
|
||||
WindowInitScrollWidgets(*this);
|
||||
}
|
||||
private:
|
||||
static constexpr int32_t ItemHeight = SCROLLABLE_ROW_HEIGHT + 1;
|
||||
static constexpr int32_t ItemCheckBoxSize = ItemHeight - 3;
|
||||
std::optional<size_t> _highlightedIndex;
|
||||
std::optional<size_t> _selectedIndex;
|
||||
|
||||
void OnClose() override
|
||||
{
|
||||
Apply();
|
||||
}
|
||||
|
||||
void OnMouseUp(WidgetIndex widgetIndex) override
|
||||
{
|
||||
switch (widgetIndex)
|
||||
public:
|
||||
void OnOpen() override
|
||||
{
|
||||
case WIDX_CLOSE:
|
||||
Close();
|
||||
break;
|
||||
case WIDX_MOVE_UP:
|
||||
ReorderSelectedAssetPack(-1);
|
||||
break;
|
||||
case WIDX_MOVE_DOWN:
|
||||
ReorderSelectedAssetPack(1);
|
||||
break;
|
||||
case WIDX_APPLY:
|
||||
Apply();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ScreenSize OnScrollGetSize(int32_t scrollIndex) override
|
||||
{
|
||||
ScreenSize result;
|
||||
auto assetPackManager = GetContext()->GetAssetPackManager();
|
||||
if (assetPackManager != nullptr)
|
||||
{
|
||||
auto numAssetPacks = assetPackManager->GetCount() + 1; // +1 for the base assets item
|
||||
result.height = static_cast<int32_t>(numAssetPacks * ItemHeight);
|
||||
widgets = WindowAssetPacksWidgets;
|
||||
WindowInitScrollWidgets(*this);
|
||||
}
|
||||
|
||||
if (_highlightedIndex)
|
||||
void OnClose() override
|
||||
{
|
||||
_highlightedIndex = {};
|
||||
Invalidate();
|
||||
Apply();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override
|
||||
{
|
||||
auto isCheckBox = false;
|
||||
auto index = GetAssetPackIndexFromPosition(screenCoords, isCheckBox);
|
||||
|
||||
// Click on checkbox
|
||||
if (index && isCheckBox)
|
||||
void OnMouseUp(WidgetIndex widgetIndex) override
|
||||
{
|
||||
switch (widgetIndex)
|
||||
{
|
||||
case WIDX_CLOSE:
|
||||
Close();
|
||||
break;
|
||||
case WIDX_MOVE_UP:
|
||||
ReorderSelectedAssetPack(-1);
|
||||
break;
|
||||
case WIDX_MOVE_DOWN:
|
||||
ReorderSelectedAssetPack(1);
|
||||
break;
|
||||
case WIDX_APPLY:
|
||||
Apply();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ScreenSize OnScrollGetSize(int32_t scrollIndex) override
|
||||
{
|
||||
ScreenSize result;
|
||||
auto assetPackManager = GetContext()->GetAssetPackManager();
|
||||
if (assetPackManager != nullptr)
|
||||
{
|
||||
auto assetPack = assetPackManager->GetAssetPack(*index);
|
||||
if (assetPack != nullptr)
|
||||
auto numAssetPacks = assetPackManager->GetCount() + 1; // +1 for the base assets item
|
||||
result.height = static_cast<int32_t>(numAssetPacks * ItemHeight);
|
||||
}
|
||||
|
||||
if (_highlightedIndex)
|
||||
{
|
||||
_highlightedIndex = {};
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void OnScrollMouseDown(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override
|
||||
{
|
||||
auto isCheckBox = false;
|
||||
auto index = GetAssetPackIndexFromPosition(screenCoords, isCheckBox);
|
||||
|
||||
// Click on checkbox
|
||||
if (index && isCheckBox)
|
||||
{
|
||||
auto assetPackManager = GetContext()->GetAssetPackManager();
|
||||
if (assetPackManager != nullptr)
|
||||
{
|
||||
assetPack->SetEnabled(!assetPack->IsEnabled());
|
||||
Invalidate();
|
||||
auto assetPack = assetPackManager->GetAssetPack(*index);
|
||||
if (assetPack != nullptr)
|
||||
{
|
||||
assetPack->SetEnabled(!assetPack->IsEnabled());
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Select item
|
||||
if (_selectedIndex != index)
|
||||
{
|
||||
_selectedIndex = index;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override
|
||||
{
|
||||
auto isCheckBox = false;
|
||||
auto index = GetAssetPackIndexFromPosition(screenCoords, isCheckBox);
|
||||
if (_highlightedIndex != index)
|
||||
{
|
||||
_highlightedIndex = index;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void OnPrepareDraw() override
|
||||
{
|
||||
ResizeFrame();
|
||||
|
||||
auto& list = widgets[WIDX_LIST];
|
||||
list.left = 6;
|
||||
list.top = 20 + 11 + 3;
|
||||
list.right = width - 2 - 24 - 1;
|
||||
list.bottom = height - 6 - 11 - 3;
|
||||
|
||||
widgets[WIDX_HIGH_LABEL].bottom = list.top - 1;
|
||||
widgets[WIDX_HIGH_LABEL].top = widgets[WIDX_HIGH_LABEL].bottom - 11 - 3;
|
||||
widgets[WIDX_HIGH_LABEL].left = list.left;
|
||||
widgets[WIDX_HIGH_LABEL].right = list.right;
|
||||
widgets[WIDX_LOW_LABEL].top = list.bottom + 1 + 3;
|
||||
widgets[WIDX_LOW_LABEL].bottom = widgets[WIDX_LOW_LABEL].top + 11 + 3;
|
||||
widgets[WIDX_LOW_LABEL].left = list.left;
|
||||
widgets[WIDX_LOW_LABEL].right = list.right;
|
||||
|
||||
auto toolstripY = 20;
|
||||
auto toolstripRight = width - 2;
|
||||
auto toolstripLeft = toolstripRight - 24;
|
||||
for (WidgetIndex i = WIDX_MOVE_UP; i <= WIDX_APPLY; i++)
|
||||
{
|
||||
widgets[i].top = toolstripY;
|
||||
widgets[i].bottom = toolstripY + 24;
|
||||
widgets[i].left = toolstripLeft;
|
||||
widgets[i].right = toolstripRight;
|
||||
toolstripY += 24;
|
||||
}
|
||||
|
||||
SetWidgetDisabled(WIDX_MOVE_UP, !_selectedIndex || _selectedIndex == 0u);
|
||||
SetWidgetDisabled(WIDX_MOVE_DOWN, !_selectedIndex || _selectedIndex >= GetNumAssetPacks() - 1);
|
||||
|
||||
widgets[WIDX_APPLY].bottom = widgets[WIDX_LIST].bottom;
|
||||
widgets[WIDX_APPLY].top = widgets[WIDX_APPLY].bottom - 24;
|
||||
}
|
||||
|
||||
void OnDraw(DrawPixelInfo& dpi) override
|
||||
{
|
||||
DrawWidgets(dpi);
|
||||
}
|
||||
|
||||
void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override
|
||||
{
|
||||
auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y };
|
||||
GfxFillRect(
|
||||
dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width - 1, dpi.height - 1 } }, ColourMapA[colours[1]].mid_light);
|
||||
|
||||
auto assetPackManager = GetContext()->GetAssetPackManager();
|
||||
if (assetPackManager == nullptr)
|
||||
return;
|
||||
|
||||
auto numAssetPacks = assetPackManager->GetCount();
|
||||
auto y = 0;
|
||||
for (size_t i = 0; i <= numAssetPacks; i++)
|
||||
{
|
||||
if (y > dpi.y + dpi.height)
|
||||
break;
|
||||
if (y + 11 < dpi.y)
|
||||
continue;
|
||||
|
||||
auto isSelected = i == _selectedIndex;
|
||||
auto isHighlighted = i == _highlightedIndex;
|
||||
if (i == numAssetPacks)
|
||||
// Select item
|
||||
if (_selectedIndex != index)
|
||||
{
|
||||
auto ft = Formatter();
|
||||
ft.Add<StringId>(STR_BASE_GRAPHICS_MUSIC_SOUND);
|
||||
PaintItem(dpi, y, ft, true, isSelected, isHighlighted);
|
||||
_selectedIndex = index;
|
||||
Invalidate();
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
void OnScrollMouseOver(int32_t scrollIndex, const ScreenCoordsXY& screenCoords) override
|
||||
{
|
||||
auto isCheckBox = false;
|
||||
auto index = GetAssetPackIndexFromPosition(screenCoords, isCheckBox);
|
||||
if (_highlightedIndex != index)
|
||||
{
|
||||
auto assetPack = assetPackManager->GetAssetPack(i);
|
||||
if (assetPack != nullptr)
|
||||
_highlightedIndex = index;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void OnPrepareDraw() override
|
||||
{
|
||||
ResizeFrame();
|
||||
|
||||
auto& list = widgets[WIDX_LIST];
|
||||
list.left = 6;
|
||||
list.top = 20 + 11 + 3;
|
||||
list.right = width - 2 - 24 - 1;
|
||||
list.bottom = height - 6 - 11 - 3;
|
||||
|
||||
widgets[WIDX_HIGH_LABEL].bottom = list.top - 1;
|
||||
widgets[WIDX_HIGH_LABEL].top = widgets[WIDX_HIGH_LABEL].bottom - 11 - 3;
|
||||
widgets[WIDX_HIGH_LABEL].left = list.left;
|
||||
widgets[WIDX_HIGH_LABEL].right = list.right;
|
||||
widgets[WIDX_LOW_LABEL].top = list.bottom + 1 + 3;
|
||||
widgets[WIDX_LOW_LABEL].bottom = widgets[WIDX_LOW_LABEL].top + 11 + 3;
|
||||
widgets[WIDX_LOW_LABEL].left = list.left;
|
||||
widgets[WIDX_LOW_LABEL].right = list.right;
|
||||
|
||||
auto toolstripY = 20;
|
||||
auto toolstripRight = width - 2;
|
||||
auto toolstripLeft = toolstripRight - 24;
|
||||
for (WidgetIndex i = WIDX_MOVE_UP; i <= WIDX_APPLY; i++)
|
||||
{
|
||||
widgets[i].top = toolstripY;
|
||||
widgets[i].bottom = toolstripY + 24;
|
||||
widgets[i].left = toolstripLeft;
|
||||
widgets[i].right = toolstripRight;
|
||||
toolstripY += 24;
|
||||
}
|
||||
|
||||
SetWidgetDisabled(WIDX_MOVE_UP, !_selectedIndex || _selectedIndex == 0u);
|
||||
SetWidgetDisabled(WIDX_MOVE_DOWN, !_selectedIndex || _selectedIndex >= GetNumAssetPacks() - 1);
|
||||
|
||||
widgets[WIDX_APPLY].bottom = widgets[WIDX_LIST].bottom;
|
||||
widgets[WIDX_APPLY].top = widgets[WIDX_APPLY].bottom - 24;
|
||||
}
|
||||
|
||||
void OnDraw(DrawPixelInfo& dpi) override
|
||||
{
|
||||
DrawWidgets(dpi);
|
||||
}
|
||||
|
||||
void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override
|
||||
{
|
||||
auto dpiCoords = ScreenCoordsXY{ dpi.x, dpi.y };
|
||||
GfxFillRect(
|
||||
dpi, { dpiCoords, dpiCoords + ScreenCoordsXY{ dpi.width - 1, dpi.height - 1 } },
|
||||
ColourMapA[colours[1]].mid_light);
|
||||
|
||||
auto assetPackManager = GetContext()->GetAssetPackManager();
|
||||
if (assetPackManager == nullptr)
|
||||
return;
|
||||
|
||||
auto numAssetPacks = assetPackManager->GetCount();
|
||||
auto y = 0;
|
||||
for (size_t i = 0; i <= numAssetPacks; i++)
|
||||
{
|
||||
if (y > dpi.y + dpi.height)
|
||||
break;
|
||||
if (y + 11 < dpi.y)
|
||||
continue;
|
||||
|
||||
auto isSelected = i == _selectedIndex;
|
||||
auto isHighlighted = i == _highlightedIndex;
|
||||
if (i == numAssetPacks)
|
||||
{
|
||||
auto isChecked = assetPack->IsEnabled();
|
||||
auto ft = Formatter();
|
||||
ft.Add<StringId>(STR_STRING);
|
||||
ft.Add<const char*>(assetPack->Name.c_str());
|
||||
PaintItem(dpi, y, ft, isChecked, isSelected, isHighlighted);
|
||||
ft.Add<StringId>(STR_BASE_GRAPHICS_MUSIC_SOUND);
|
||||
PaintItem(dpi, y, ft, true, isSelected, isHighlighted);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto assetPack = assetPackManager->GetAssetPack(i);
|
||||
if (assetPack != nullptr)
|
||||
{
|
||||
auto isChecked = assetPack->IsEnabled();
|
||||
auto ft = Formatter();
|
||||
ft.Add<StringId>(STR_STRING);
|
||||
ft.Add<const char*>(assetPack->Name.c_str());
|
||||
PaintItem(dpi, y, ft, isChecked, isSelected, isHighlighted);
|
||||
}
|
||||
}
|
||||
y += ItemHeight;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void PaintItem(DrawPixelInfo& dpi, int32_t y, Formatter& ft, bool isChecked, bool isSelected, bool isHighlighted)
|
||||
{
|
||||
auto listWidth = dpi.width - 1;
|
||||
auto stringId = STR_BLACK_STRING;
|
||||
auto fillRectangle = ScreenRect{ { 0, y }, { listWidth, y + ItemHeight - 1 } };
|
||||
if (isSelected)
|
||||
{
|
||||
GfxFillRect(dpi, fillRectangle, ColourMapA[colours[1]].mid_dark);
|
||||
stringId = STR_WINDOW_COLOUR_2_STRINGID;
|
||||
}
|
||||
else if (isHighlighted)
|
||||
{
|
||||
GfxFillRect(dpi, fillRectangle, ColourMapA[colours[1]].mid_dark);
|
||||
}
|
||||
|
||||
DrawTextEllipsised(dpi, { 16, y + 1 }, listWidth, stringId, ft);
|
||||
|
||||
auto checkboxSize = ItemHeight - 3;
|
||||
PaintCheckbox(dpi, { { 2, y + 1 }, { 2 + checkboxSize + 1, y + 1 + checkboxSize } }, isChecked);
|
||||
}
|
||||
|
||||
void PaintCheckbox(DrawPixelInfo& dpi, const ScreenRect& rect, bool checked)
|
||||
{
|
||||
GfxFillRectInset(dpi, rect, colours[1], INSET_RECT_F_E0);
|
||||
if (checked)
|
||||
{
|
||||
auto checkmark = Formatter();
|
||||
checkmark.Add<StringId>(STR_STRING);
|
||||
checkmark.Add<char*>(CheckBoxMarkString);
|
||||
DrawTextBasic(
|
||||
dpi, ScreenCoordsXY{ rect.GetLeft() + 1, rect.GetTop() }, STR_WINDOW_COLOUR_2_STRINGID, checkmark);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<size_t> GetAssetPackIndexFromPosition(const ScreenCoordsXY& pos, bool& isCheckBox)
|
||||
{
|
||||
const auto index = pos.y / ItemHeight;
|
||||
if (index < 0 || static_cast<size_t>(index) >= GetNumAssetPacks())
|
||||
return std::nullopt;
|
||||
|
||||
isCheckBox = pos.x >= 2 && pos.x <= 2 + ItemCheckBoxSize + 1;
|
||||
return static_cast<size_t>(index);
|
||||
}
|
||||
|
||||
size_t GetNumAssetPacks() const
|
||||
{
|
||||
auto assetPackManager = GetContext()->GetAssetPackManager();
|
||||
if (assetPackManager == nullptr)
|
||||
return 0;
|
||||
return assetPackManager->GetCount();
|
||||
}
|
||||
|
||||
bool IsSelectedAssetPackEnabled() const
|
||||
{
|
||||
if (_selectedIndex)
|
||||
{
|
||||
auto assetPackManager = GetContext()->GetAssetPackManager();
|
||||
if (assetPackManager != nullptr)
|
||||
{
|
||||
auto assetPack = assetPackManager->GetAssetPack(*_selectedIndex);
|
||||
if (assetPack != nullptr)
|
||||
{
|
||||
return assetPack->IsEnabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
y += ItemHeight;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void PaintItem(DrawPixelInfo& dpi, int32_t y, Formatter& ft, bool isChecked, bool isSelected, bool isHighlighted)
|
||||
{
|
||||
auto listWidth = dpi.width - 1;
|
||||
auto stringId = STR_BLACK_STRING;
|
||||
auto fillRectangle = ScreenRect{ { 0, y }, { listWidth, y + ItemHeight - 1 } };
|
||||
if (isSelected)
|
||||
{
|
||||
GfxFillRect(dpi, fillRectangle, ColourMapA[colours[1]].mid_dark);
|
||||
stringId = STR_WINDOW_COLOUR_2_STRINGID;
|
||||
}
|
||||
else if (isHighlighted)
|
||||
{
|
||||
GfxFillRect(dpi, fillRectangle, ColourMapA[colours[1]].mid_dark);
|
||||
return false;
|
||||
}
|
||||
|
||||
DrawTextEllipsised(dpi, { 16, y + 1 }, listWidth, stringId, ft);
|
||||
|
||||
auto checkboxSize = ItemHeight - 3;
|
||||
PaintCheckbox(dpi, { { 2, y + 1 }, { 2 + checkboxSize + 1, y + 1 + checkboxSize } }, isChecked);
|
||||
}
|
||||
|
||||
void PaintCheckbox(DrawPixelInfo& dpi, const ScreenRect& rect, bool checked)
|
||||
{
|
||||
GfxFillRectInset(dpi, rect, colours[1], INSET_RECT_F_E0);
|
||||
if (checked)
|
||||
void ReorderSelectedAssetPack(int32_t direction)
|
||||
{
|
||||
auto checkmark = Formatter();
|
||||
checkmark.Add<StringId>(STR_STRING);
|
||||
checkmark.Add<char*>(CheckBoxMarkString);
|
||||
DrawTextBasic(dpi, ScreenCoordsXY{ rect.GetLeft() + 1, rect.GetTop() }, STR_WINDOW_COLOUR_2_STRINGID, checkmark);
|
||||
if (!_selectedIndex)
|
||||
return;
|
||||
|
||||
auto assetPackManager = GetContext()->GetAssetPackManager();
|
||||
if (assetPackManager == nullptr)
|
||||
return;
|
||||
|
||||
if (direction < 0 && *_selectedIndex > 0)
|
||||
{
|
||||
assetPackManager->Swap(*_selectedIndex, *_selectedIndex - 1);
|
||||
(*_selectedIndex)--;
|
||||
Invalidate();
|
||||
}
|
||||
else if (direction > 0 && *_selectedIndex < assetPackManager->GetCount() - 1)
|
||||
{
|
||||
assetPackManager->Swap(*_selectedIndex, *_selectedIndex + 1);
|
||||
(*_selectedIndex)++;
|
||||
Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<size_t> GetAssetPackIndexFromPosition(const ScreenCoordsXY& pos, bool& isCheckBox)
|
||||
{
|
||||
const auto index = pos.y / ItemHeight;
|
||||
if (index < 0 || static_cast<size_t>(index) >= GetNumAssetPacks())
|
||||
return std::nullopt;
|
||||
|
||||
isCheckBox = pos.x >= 2 && pos.x <= 2 + ItemCheckBoxSize + 1;
|
||||
return static_cast<size_t>(index);
|
||||
}
|
||||
|
||||
size_t GetNumAssetPacks() const
|
||||
{
|
||||
auto assetPackManager = GetContext()->GetAssetPackManager();
|
||||
if (assetPackManager == nullptr)
|
||||
return 0;
|
||||
return assetPackManager->GetCount();
|
||||
}
|
||||
|
||||
bool IsSelectedAssetPackEnabled() const
|
||||
{
|
||||
if (_selectedIndex)
|
||||
void Apply()
|
||||
{
|
||||
auto& objectManager = GetContext()->GetObjectManager();
|
||||
objectManager.ResetObjects();
|
||||
|
||||
auto assetPackManager = GetContext()->GetAssetPackManager();
|
||||
if (assetPackManager != nullptr)
|
||||
{
|
||||
auto assetPack = assetPackManager->GetAssetPack(*_selectedIndex);
|
||||
if (assetPack != nullptr)
|
||||
{
|
||||
return assetPack->IsEnabled();
|
||||
}
|
||||
assetPackManager->SaveEnabledAssetPacks();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
void ReorderSelectedAssetPack(int32_t direction)
|
||||
WindowBase* AssetPacksOpen()
|
||||
{
|
||||
if (!_selectedIndex)
|
||||
return;
|
||||
|
||||
auto assetPackManager = GetContext()->GetAssetPackManager();
|
||||
if (assetPackManager == nullptr)
|
||||
return;
|
||||
|
||||
if (direction < 0 && *_selectedIndex > 0)
|
||||
{
|
||||
assetPackManager->Swap(*_selectedIndex, *_selectedIndex - 1);
|
||||
(*_selectedIndex)--;
|
||||
Invalidate();
|
||||
}
|
||||
else if (direction > 0 && *_selectedIndex < assetPackManager->GetCount() - 1)
|
||||
{
|
||||
assetPackManager->Swap(*_selectedIndex, *_selectedIndex + 1);
|
||||
(*_selectedIndex)++;
|
||||
Invalidate();
|
||||
}
|
||||
auto flags = WF_AUTO_POSITION | WF_CENTRE_SCREEN;
|
||||
return WindowFocusOrCreate<AssetPacksWindow>(WindowClass::AssetPacks, WW, WH, flags);
|
||||
}
|
||||
|
||||
void Apply()
|
||||
{
|
||||
auto& objectManager = GetContext()->GetObjectManager();
|
||||
objectManager.ResetObjects();
|
||||
|
||||
auto assetPackManager = GetContext()->GetAssetPackManager();
|
||||
if (assetPackManager != nullptr)
|
||||
{
|
||||
assetPackManager->SaveEnabledAssetPacks();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WindowBase* WindowAssetPacksOpen()
|
||||
{
|
||||
auto flags = WF_AUTO_POSITION | WF_CENTRE_SCREEN;
|
||||
return WindowFocusOrCreate<AssetPacksWindow>(WindowClass::AssetPacks, WW, WH, flags);
|
||||
}
|
||||
} // namespace OpenRCT2::Ui::Windows
|
||||
|
|
|
@ -22,12 +22,13 @@
|
|||
#include <openrct2/sprites.h>
|
||||
#include <openrct2/world/Banner.h>
|
||||
#include <openrct2/world/Scenery.h>
|
||||
namespace OpenRCT2::Ui::Windows
|
||||
{
|
||||
static constexpr int32_t WW = 113;
|
||||
static constexpr int32_t WH = 96;
|
||||
static constexpr StringId WINDOW_TITLE = STR_BANNER_WINDOW_TITLE;
|
||||
|
||||
static constexpr int32_t WW = 113;
|
||||
static constexpr int32_t WH = 96;
|
||||
static constexpr StringId WINDOW_TITLE = STR_BANNER_WINDOW_TITLE;
|
||||
|
||||
// clang-format off
|
||||
// clang-format off
|
||||
enum WindowBannerWidgetIdx {
|
||||
WIDX_BACKGROUND,
|
||||
WIDX_TITLE,
|
||||
|
@ -67,256 +68,258 @@ static Widget window_banner_widgets[] = {
|
|||
MakeWidget({ 5, WH - 16}, {12, 12}, WindowWidgetType::ColourBtn, WindowColour::Secondary, 0xFFFFFFFF, STR_SELECT_MAIN_SIGN_COLOUR_TIP), // high money
|
||||
MakeWidget({ 43, WH - 16}, {39, 12}, WindowWidgetType::DropdownMenu, WindowColour::Secondary ), // high money
|
||||
MakeWidget({ 70, WH - 15}, {11, 10}, WindowWidgetType::Button, WindowColour::Secondary, STR_DROPDOWN_GLYPH, STR_SELECT_TEXT_COLOUR_TIP ), // high money
|
||||
WIDGETS_END,
|
||||
kWidgetsEnd,
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
// clang-format on
|
||||
|
||||
class BannerWindow final : public Window
|
||||
{
|
||||
private:
|
||||
CoordsXYZ _bannerViewPos;
|
||||
|
||||
void CreateViewport()
|
||||
class BannerWindow final : public Window
|
||||
{
|
||||
Widget* viewportWidget = &window_banner_widgets[WIDX_VIEWPORT];
|
||||
ViewportCreate(
|
||||
this, windowPos + ScreenCoordsXY{ viewportWidget->left + 1, viewportWidget->top + 1 },
|
||||
(viewportWidget->width()) - 1, (viewportWidget->height()) - 1, Focus(_bannerViewPos));
|
||||
private:
|
||||
CoordsXYZ _bannerViewPos;
|
||||
|
||||
if (viewport != nullptr)
|
||||
viewport->flags = gConfigGeneral.AlwaysShowGridlines ? VIEWPORT_FLAG_GRIDLINES : 0;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
BannerIndex GetBannerIndex() const
|
||||
{
|
||||
return BannerIndex::FromUnderlying(number);
|
||||
}
|
||||
|
||||
BannerElement* GetBannerElement()
|
||||
{
|
||||
auto* banner = GetBanner(GetBannerIndex());
|
||||
if (banner == nullptr)
|
||||
void CreateViewport()
|
||||
{
|
||||
return nullptr;
|
||||
Widget* viewportWidget = &window_banner_widgets[WIDX_VIEWPORT];
|
||||
ViewportCreate(
|
||||
this, windowPos + ScreenCoordsXY{ viewportWidget->left + 1, viewportWidget->top + 1 },
|
||||
(viewportWidget->width()) - 1, (viewportWidget->height()) - 1, Focus(_bannerViewPos));
|
||||
|
||||
if (viewport != nullptr)
|
||||
viewport->flags = gConfigGeneral.AlwaysShowGridlines ? VIEWPORT_FLAG_GRIDLINES : VIEWPORT_FLAG_NONE;
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
TileElement* tileElement = MapGetFirstElementAt(banner->position);
|
||||
if (tileElement == nullptr)
|
||||
BannerIndex GetBannerIndex() const
|
||||
{
|
||||
return nullptr;
|
||||
return BannerIndex::FromUnderlying(number);
|
||||
}
|
||||
|
||||
do
|
||||
BannerElement* GetBannerElement()
|
||||
{
|
||||
auto* bannerElement = tileElement->AsBanner();
|
||||
if (bannerElement == nullptr)
|
||||
auto* banner = GetBanner(GetBannerIndex());
|
||||
if (banner == nullptr)
|
||||
{
|
||||
continue;
|
||||
return nullptr;
|
||||
}
|
||||
if (bannerElement->GetIndex() == GetBannerIndex())
|
||||
|
||||
TileElement* tileElement = MapGetFirstElementAt(banner->position);
|
||||
if (tileElement == nullptr)
|
||||
{
|
||||
return bannerElement;
|
||||
return nullptr;
|
||||
}
|
||||
} while (!(tileElement++)->IsLastForTile());
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
void OnOpen() override
|
||||
{
|
||||
widgets = window_banner_widgets;
|
||||
WindowInitScrollWidgets(*this);
|
||||
}
|
||||
|
||||
void Initialise(rct_windownumber _number)
|
||||
{
|
||||
number = _number;
|
||||
auto* banner = GetBanner(BannerIndex::FromUnderlying(number));
|
||||
|
||||
auto* bannerElement = GetBannerElement();
|
||||
if (bannerElement == nullptr)
|
||||
return;
|
||||
|
||||
_bannerViewPos = CoordsXYZ{ banner->position.ToCoordsXY().ToTileCentre(), bannerElement->GetBaseZ() };
|
||||
CreateViewport();
|
||||
}
|
||||
|
||||
void OnMouseDown(WidgetIndex widgetIndex) override
|
||||
{
|
||||
Widget* widget = &widgets[widgetIndex];
|
||||
auto* banner = GetBanner(GetBannerIndex());
|
||||
if (banner == nullptr)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
switch (widgetIndex)
|
||||
{
|
||||
case WIDX_MAIN_COLOUR:
|
||||
WindowDropdownShowColour(this, widget, TRANSLUCENT(colours[1]), banner->colour);
|
||||
break;
|
||||
case WIDX_TEXT_COLOUR_DROPDOWN_BUTTON:
|
||||
|
||||
for (int32_t i = 0; i < 13; ++i)
|
||||
{
|
||||
gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL;
|
||||
gDropdownItems[i].Args = BannerColouredTextFormats[i + 1];
|
||||
}
|
||||
|
||||
// Switch to the dropdown box widget.
|
||||
widget--;
|
||||
|
||||
WindowDropdownShowTextCustomWidth(
|
||||
{ widget->left + windowPos.x, widget->top + windowPos.y }, widget->height() + 1, colours[1], 0,
|
||||
Dropdown::Flag::StayOpen, 13, widget->width() - 3);
|
||||
|
||||
Dropdown::SetChecked(banner->text_colour - 1, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnMouseUp(WidgetIndex widgetIndex) override
|
||||
{
|
||||
auto* banner = GetBanner(GetBannerIndex());
|
||||
if (banner == nullptr)
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
switch (widgetIndex)
|
||||
{
|
||||
case WIDX_CLOSE:
|
||||
Close();
|
||||
break;
|
||||
case WIDX_BANNER_DEMOLISH:
|
||||
do
|
||||
{
|
||||
auto* bannerElement = GetBannerElement();
|
||||
auto* bannerElement = tileElement->AsBanner();
|
||||
if (bannerElement == nullptr)
|
||||
break;
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (bannerElement->GetIndex() == GetBannerIndex())
|
||||
{
|
||||
return bannerElement;
|
||||
}
|
||||
} while (!(tileElement++)->IsLastForTile());
|
||||
|
||||
auto bannerRemoveAction = BannerRemoveAction(
|
||||
{ banner->position.ToCoordsXY(), bannerElement->GetBaseZ(), bannerElement->GetPosition() });
|
||||
GameActions::Execute(&bannerRemoveAction);
|
||||
break;
|
||||
}
|
||||
case WIDX_BANNER_TEXT:
|
||||
WindowTextInputRawOpen(
|
||||
this, WIDX_BANNER_TEXT, STR_BANNER_TEXT, STR_ENTER_BANNER_TEXT, {}, banner->GetText().c_str(), 32);
|
||||
break;
|
||||
case WIDX_BANNER_NO_ENTRY:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
public:
|
||||
void OnOpen() override
|
||||
{
|
||||
widgets = window_banner_widgets;
|
||||
WindowInitScrollWidgets(*this);
|
||||
}
|
||||
|
||||
void Initialise(rct_windownumber _number)
|
||||
{
|
||||
number = _number;
|
||||
auto* banner = GetBanner(BannerIndex::FromUnderlying(number));
|
||||
|
||||
auto* bannerElement = GetBannerElement();
|
||||
if (bannerElement == nullptr)
|
||||
return;
|
||||
|
||||
_bannerViewPos = CoordsXYZ{ banner->position.ToCoordsXY().ToTileCentre(), bannerElement->GetBaseZ() };
|
||||
CreateViewport();
|
||||
}
|
||||
|
||||
void OnMouseDown(WidgetIndex widgetIndex) override
|
||||
{
|
||||
Widget* widget = &widgets[widgetIndex];
|
||||
auto* banner = GetBanner(GetBannerIndex());
|
||||
if (banner == nullptr)
|
||||
{
|
||||
TextinputCancel();
|
||||
auto bannerSetStyle = BannerSetStyleAction(
|
||||
BannerSetStyleType::NoEntry, GetBannerIndex(), banner->flags ^ BANNER_FLAG_NO_ENTRY);
|
||||
GameActions::Execute(&bannerSetStyle);
|
||||
break;
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override
|
||||
{
|
||||
switch (widgetIndex)
|
||||
{
|
||||
case WIDX_MAIN_COLOUR:
|
||||
switch (widgetIndex)
|
||||
{
|
||||
if (dropdownIndex == -1)
|
||||
case WIDX_MAIN_COLOUR:
|
||||
WindowDropdownShowColour(this, widget, TRANSLUCENT(colours[1]), banner->colour);
|
||||
break;
|
||||
case WIDX_TEXT_COLOUR_DROPDOWN_BUTTON:
|
||||
|
||||
auto bannerSetStyle = BannerSetStyleAction(
|
||||
BannerSetStyleType::PrimaryColour, GetBannerIndex(), ColourDropDownIndexToColour(dropdownIndex));
|
||||
GameActions::Execute(&bannerSetStyle);
|
||||
break;
|
||||
for (int32_t i = 0; i < 13; ++i)
|
||||
{
|
||||
gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL;
|
||||
gDropdownItems[i].Args = BannerColouredTextFormats[i + 1];
|
||||
}
|
||||
|
||||
// Switch to the dropdown box widget.
|
||||
widget--;
|
||||
|
||||
WindowDropdownShowTextCustomWidth(
|
||||
{ widget->left + windowPos.x, widget->top + windowPos.y }, widget->height() + 1, colours[1], 0,
|
||||
Dropdown::Flag::StayOpen, 13, widget->width() - 3);
|
||||
|
||||
Dropdown::SetChecked(banner->text_colour - 1, true);
|
||||
break;
|
||||
}
|
||||
case WIDX_TEXT_COLOUR_DROPDOWN_BUTTON:
|
||||
}
|
||||
|
||||
void OnMouseUp(WidgetIndex widgetIndex) override
|
||||
{
|
||||
auto* banner = GetBanner(GetBannerIndex());
|
||||
if (banner == nullptr)
|
||||
{
|
||||
if (dropdownIndex == -1)
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
switch (widgetIndex)
|
||||
{
|
||||
case WIDX_CLOSE:
|
||||
Close();
|
||||
break;
|
||||
auto bannerSetStyle = BannerSetStyleAction(BannerSetStyleType::TextColour, GetBannerIndex(), dropdownIndex + 1);
|
||||
GameActions::Execute(&bannerSetStyle);
|
||||
break;
|
||||
case WIDX_BANNER_DEMOLISH:
|
||||
{
|
||||
auto* bannerElement = GetBannerElement();
|
||||
if (bannerElement == nullptr)
|
||||
break;
|
||||
|
||||
auto bannerRemoveAction = BannerRemoveAction(
|
||||
{ banner->position.ToCoordsXY(), bannerElement->GetBaseZ(), bannerElement->GetPosition() });
|
||||
GameActions::Execute(&bannerRemoveAction);
|
||||
break;
|
||||
}
|
||||
case WIDX_BANNER_TEXT:
|
||||
WindowTextInputRawOpen(
|
||||
this, WIDX_BANNER_TEXT, STR_BANNER_TEXT, STR_ENTER_BANNER_TEXT, {}, banner->GetText().c_str(), 32);
|
||||
break;
|
||||
case WIDX_BANNER_NO_ENTRY:
|
||||
{
|
||||
TextinputCancel();
|
||||
auto bannerSetStyle = BannerSetStyleAction(
|
||||
BannerSetStyleType::NoEntry, GetBannerIndex(), banner->flags ^ BANNER_FLAG_NO_ENTRY);
|
||||
GameActions::Execute(&bannerSetStyle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override
|
||||
{
|
||||
if (widgetIndex == WIDX_BANNER_TEXT)
|
||||
void OnDropdown(WidgetIndex widgetIndex, int32_t dropdownIndex) override
|
||||
{
|
||||
auto bannerSetNameAction = BannerSetNameAction(GetBannerIndex(), std::string(text));
|
||||
GameActions::Execute(&bannerSetNameAction);
|
||||
switch (widgetIndex)
|
||||
{
|
||||
case WIDX_MAIN_COLOUR:
|
||||
{
|
||||
if (dropdownIndex == -1)
|
||||
break;
|
||||
|
||||
auto bannerSetStyle = BannerSetStyleAction(
|
||||
BannerSetStyleType::PrimaryColour, GetBannerIndex(), ColourDropDownIndexToColour(dropdownIndex));
|
||||
GameActions::Execute(&bannerSetStyle);
|
||||
break;
|
||||
}
|
||||
case WIDX_TEXT_COLOUR_DROPDOWN_BUTTON:
|
||||
{
|
||||
if (dropdownIndex == -1)
|
||||
break;
|
||||
auto bannerSetStyle = BannerSetStyleAction(
|
||||
BannerSetStyleType::TextColour, GetBannerIndex(), dropdownIndex + 1);
|
||||
GameActions::Execute(&bannerSetStyle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnViewportRotate() override
|
||||
{
|
||||
RemoveViewport();
|
||||
CreateViewport();
|
||||
}
|
||||
|
||||
void OnDraw(DrawPixelInfo& dpi) override
|
||||
{
|
||||
DrawWidgets(dpi);
|
||||
|
||||
if (viewport != nullptr)
|
||||
void OnTextInput(WidgetIndex widgetIndex, std::string_view text) override
|
||||
{
|
||||
WindowDrawViewport(dpi, *this);
|
||||
if (widgetIndex == WIDX_BANNER_TEXT)
|
||||
{
|
||||
auto bannerSetNameAction = BannerSetNameAction(GetBannerIndex(), std::string(text));
|
||||
GameActions::Execute(&bannerSetNameAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnPrepareDraw() override
|
||||
void OnViewportRotate() override
|
||||
{
|
||||
RemoveViewport();
|
||||
CreateViewport();
|
||||
}
|
||||
|
||||
void OnDraw(DrawPixelInfo& dpi) override
|
||||
{
|
||||
DrawWidgets(dpi);
|
||||
|
||||
if (viewport != nullptr)
|
||||
{
|
||||
WindowDrawViewport(dpi, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void OnPrepareDraw() override
|
||||
{
|
||||
auto* banner = GetBanner(GetBannerIndex());
|
||||
if (banner == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Widget* colourBtn = &window_banner_widgets[WIDX_MAIN_COLOUR];
|
||||
colourBtn->type = WindowWidgetType::Empty;
|
||||
|
||||
auto* bannerEntry = OpenRCT2::ObjectManager::GetObjectEntry<BannerSceneryEntry>(banner->type);
|
||||
if (bannerEntry != nullptr && (bannerEntry->flags & BANNER_ENTRY_FLAG_HAS_PRIMARY_COLOUR))
|
||||
{
|
||||
colourBtn->type = WindowWidgetType::ColourBtn;
|
||||
}
|
||||
pressed_widgets &= ~(1uLL << WIDX_BANNER_NO_ENTRY);
|
||||
disabled_widgets &= ~(
|
||||
(1uLL << WIDX_BANNER_TEXT) | (1uLL << WIDX_TEXT_COLOUR_DROPDOWN) | (1uLL << WIDX_TEXT_COLOUR_DROPDOWN_BUTTON));
|
||||
if (banner->flags & BANNER_FLAG_NO_ENTRY)
|
||||
{
|
||||
pressed_widgets |= (1uLL << WIDX_BANNER_NO_ENTRY);
|
||||
disabled_widgets |= (1uLL << WIDX_BANNER_TEXT) | (1uLL << WIDX_TEXT_COLOUR_DROPDOWN)
|
||||
| (1uLL << WIDX_TEXT_COLOUR_DROPDOWN_BUTTON);
|
||||
}
|
||||
colourBtn->image = GetColourButtonImage(banner->colour);
|
||||
Widget* dropDownWidget = &window_banner_widgets[WIDX_TEXT_COLOUR_DROPDOWN];
|
||||
dropDownWidget->text = BannerColouredTextFormats[banner->text_colour];
|
||||
}
|
||||
|
||||
void OnResize() override
|
||||
{
|
||||
ResizeFrame();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006BA305
|
||||
*/
|
||||
WindowBase* BannerOpen(rct_windownumber number)
|
||||
{
|
||||
auto* banner = GetBanner(GetBannerIndex());
|
||||
if (banner == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Widget* colourBtn = &window_banner_widgets[WIDX_MAIN_COLOUR];
|
||||
colourBtn->type = WindowWidgetType::Empty;
|
||||
auto w = static_cast<BannerWindow*>(WindowBringToFrontByNumber(WindowClass::Banner, number));
|
||||
|
||||
auto* bannerEntry = OpenRCT2::ObjectManager::GetObjectEntry<BannerSceneryEntry>(banner->type);
|
||||
if (bannerEntry != nullptr && (bannerEntry->flags & BANNER_ENTRY_FLAG_HAS_PRIMARY_COLOUR))
|
||||
{
|
||||
colourBtn->type = WindowWidgetType::ColourBtn;
|
||||
}
|
||||
pressed_widgets &= ~(1uLL << WIDX_BANNER_NO_ENTRY);
|
||||
disabled_widgets &= ~(
|
||||
(1uLL << WIDX_BANNER_TEXT) | (1uLL << WIDX_TEXT_COLOUR_DROPDOWN) | (1uLL << WIDX_TEXT_COLOUR_DROPDOWN_BUTTON));
|
||||
if (banner->flags & BANNER_FLAG_NO_ENTRY)
|
||||
{
|
||||
pressed_widgets |= (1uLL << WIDX_BANNER_NO_ENTRY);
|
||||
disabled_widgets |= (1uLL << WIDX_BANNER_TEXT) | (1uLL << WIDX_TEXT_COLOUR_DROPDOWN)
|
||||
| (1uLL << WIDX_TEXT_COLOUR_DROPDOWN_BUTTON);
|
||||
}
|
||||
colourBtn->image = GetColourButtonImage(banner->colour);
|
||||
Widget* dropDownWidget = &window_banner_widgets[WIDX_TEXT_COLOUR_DROPDOWN];
|
||||
dropDownWidget->text = BannerColouredTextFormats[banner->text_colour];
|
||||
}
|
||||
if (w != nullptr)
|
||||
return w;
|
||||
|
||||
void OnResize() override
|
||||
{
|
||||
ResizeFrame();
|
||||
}
|
||||
};
|
||||
w = WindowCreate<BannerWindow>(WindowClass::Banner, WW, WH, 0);
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006BA305
|
||||
*/
|
||||
WindowBase* WindowBannerOpen(rct_windownumber number)
|
||||
{
|
||||
auto w = static_cast<BannerWindow*>(WindowBringToFrontByNumber(WindowClass::Banner, number));
|
||||
if (w != nullptr)
|
||||
w->Initialise(number);
|
||||
|
||||
if (w != nullptr)
|
||||
return w;
|
||||
|
||||
w = WindowCreate<BannerWindow>(WindowClass::Banner, WW, WH, 0);
|
||||
|
||||
if (w != nullptr)
|
||||
w->Initialise(number);
|
||||
|
||||
return w;
|
||||
}
|
||||
}
|
||||
} // namespace OpenRCT2::Ui::Windows
|
||||
|
|
|
@ -25,9 +25,9 @@
|
|||
#include <openrct2/util/Util.h>
|
||||
#include <vector>
|
||||
|
||||
using namespace OpenRCT2;
|
||||
|
||||
// clang-format off
|
||||
namespace OpenRCT2::Ui::Windows
|
||||
{
|
||||
// clang-format off
|
||||
enum {
|
||||
WIDX_BACKGROUND,
|
||||
WIDX_TITLE,
|
||||
|
@ -48,292 +48,293 @@ static Widget _windowChangelogWidgets[] = {
|
|||
MakeWidget({0, 14}, {500, 382}, WindowWidgetType::Resize, WindowColour::Secondary ), // content panel
|
||||
MakeWidget({3, 16}, {495, 366}, WindowWidgetType::Scroll, WindowColour::Secondary, SCROLL_BOTH ), // scroll area
|
||||
MakeWidget({3, 473}, {300, 14}, WindowWidgetType::Placeholder, WindowColour::Secondary, STR_NEW_RELEASE_DOWNLOAD_PAGE), // changelog button
|
||||
WIDGETS_END,
|
||||
kWidgetsEnd,
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
// clang-format on
|
||||
|
||||
class ChangelogWindow final : public Window
|
||||
{
|
||||
const NewVersionInfo* _newVersionInfo;
|
||||
std::vector<std::string> _changelogLines;
|
||||
int32_t _changelogLongestLineWidth = 0;
|
||||
int _personality = 0;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Retrieves the changelog contents.
|
||||
*/
|
||||
const std::string GetText(PATHID pathId)
|
||||
class ChangelogWindow final : public Window
|
||||
{
|
||||
auto env = GetContext()->GetPlatformEnvironment();
|
||||
auto path = env->GetFilePath(pathId);
|
||||
auto fs = std::ifstream(fs::u8path(path), std::ios::in);
|
||||
if (!fs.is_open())
|
||||
const NewVersionInfo* _newVersionInfo;
|
||||
std::vector<std::string> _changelogLines;
|
||||
int32_t _changelogLongestLineWidth = 0;
|
||||
int _personality = 0;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Retrieves the changelog contents.
|
||||
*/
|
||||
const std::string GetText(PATHID pathId)
|
||||
{
|
||||
throw std::runtime_error("Unable to open " + path);
|
||||
auto env = GetContext()->GetPlatformEnvironment();
|
||||
auto path = env->GetFilePath(pathId);
|
||||
auto fs = std::ifstream(fs::u8path(path), std::ios::in);
|
||||
if (!fs.is_open())
|
||||
{
|
||||
throw std::runtime_error("Unable to open " + path);
|
||||
}
|
||||
fs.seekg(0, fs.end);
|
||||
auto length = fs.tellg();
|
||||
fs.seekg(0, fs.beg);
|
||||
std::unique_ptr<char[]> buffer = std::make_unique<char[]>(length);
|
||||
fs.read(buffer.get(), length);
|
||||
auto result = std::string(buffer.get(), buffer.get() + length);
|
||||
return result;
|
||||
}
|
||||
fs.seekg(0, fs.end);
|
||||
auto length = fs.tellg();
|
||||
fs.seekg(0, fs.beg);
|
||||
std::unique_ptr<char[]> buffer = std::make_unique<char[]>(length);
|
||||
fs.read(buffer.get(), length);
|
||||
auto result = std::string(buffer.get(), buffer.get() + length);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the Changelog Window's Personality, should be called just after creation. Returns true on success
|
||||
*
|
||||
* @param personality
|
||||
*/
|
||||
bool SetPersonality(int personality)
|
||||
{
|
||||
switch (personality)
|
||||
/**
|
||||
* @brief Set the Changelog Window's Personality, should be called just after creation. Returns true on success
|
||||
*
|
||||
* @param personality
|
||||
*/
|
||||
bool SetPersonality(int personality)
|
||||
{
|
||||
case WV_NEW_VERSION_INFO:
|
||||
if (!GetContext()->HasNewVersionInfo())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_personality = WV_NEW_VERSION_INFO;
|
||||
NewVersionProcessInfo();
|
||||
widgets[WIDX_OPEN_URL].type = WindowWidgetType::Button;
|
||||
return true;
|
||||
switch (personality)
|
||||
{
|
||||
case WV_NEW_VERSION_INFO:
|
||||
if (!GetContext()->HasNewVersionInfo())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_personality = WV_NEW_VERSION_INFO;
|
||||
NewVersionProcessInfo();
|
||||
widgets[WIDX_OPEN_URL].type = WindowWidgetType::Button;
|
||||
return true;
|
||||
|
||||
case WV_CHANGELOG:
|
||||
if (!ReadFile(PATHID::CHANGELOG))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_personality = WV_CHANGELOG;
|
||||
widgets[WIDX_TITLE].text = STR_CHANGELOG_TITLE;
|
||||
return true;
|
||||
case WV_CHANGELOG:
|
||||
if (!ReadFile(PATHID::CHANGELOG))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_personality = WV_CHANGELOG;
|
||||
widgets[WIDX_TITLE].text = STR_CHANGELOG_TITLE;
|
||||
return true;
|
||||
|
||||
case WV_CONTRIBUTORS:
|
||||
if (!ReadFile(PATHID::CONTRIBUTORS))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_personality = WV_CONTRIBUTORS;
|
||||
widgets[WIDX_TITLE].text = STR_CONTRIBUTORS_WINDOW;
|
||||
return true;
|
||||
case WV_CONTRIBUTORS:
|
||||
if (!ReadFile(PATHID::CONTRIBUTORS))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_personality = WV_CONTRIBUTORS;
|
||||
widgets[WIDX_TITLE].text = STR_CONTRIBUTORS_WINDOW;
|
||||
return true;
|
||||
|
||||
default:
|
||||
LOG_ERROR("Invalid personality for changelog window: %d", personality);
|
||||
default:
|
||||
LOG_ERROR("Invalid personality for changelog window: %d", personality);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void OnOpen() override
|
||||
{
|
||||
widgets = _windowChangelogWidgets;
|
||||
|
||||
WindowInitScrollWidgets(*this);
|
||||
min_width = MIN_WW;
|
||||
min_height = MIN_WH;
|
||||
max_width = MIN_WW;
|
||||
max_height = MIN_WH;
|
||||
}
|
||||
|
||||
void OnResize() override
|
||||
{
|
||||
int32_t screenWidth = ContextGetWidth();
|
||||
int32_t screenHeight = ContextGetHeight();
|
||||
|
||||
max_width = (screenWidth * 4) / 5;
|
||||
max_height = (screenHeight * 4) / 5;
|
||||
|
||||
min_width = MIN_WW;
|
||||
min_height = MIN_WH;
|
||||
|
||||
auto download_button_width = widgets[WIDX_OPEN_URL].width();
|
||||
widgets[WIDX_OPEN_URL].left = (width - download_button_width) / 2;
|
||||
widgets[WIDX_OPEN_URL].right = widgets[WIDX_OPEN_URL].left + download_button_width;
|
||||
|
||||
if (width < min_width)
|
||||
{
|
||||
Invalidate();
|
||||
width = min_width;
|
||||
}
|
||||
if (height < min_height)
|
||||
{
|
||||
Invalidate();
|
||||
height = min_height;
|
||||
}
|
||||
}
|
||||
|
||||
void OnPrepareDraw() override
|
||||
{
|
||||
ResizeFrameWithPage();
|
||||
widgets[WIDX_SCROLL].right = width - 3;
|
||||
widgets[WIDX_SCROLL].bottom = height - 22;
|
||||
widgets[WIDX_OPEN_URL].bottom = height - 5;
|
||||
widgets[WIDX_OPEN_URL].top = height - 19;
|
||||
}
|
||||
|
||||
void OnMouseUp(WidgetIndex widgetIndex) override
|
||||
{
|
||||
switch (widgetIndex)
|
||||
{
|
||||
case WIDX_CLOSE:
|
||||
Close();
|
||||
break;
|
||||
case WIDX_OPEN_URL:
|
||||
if (_newVersionInfo != nullptr)
|
||||
{
|
||||
GetContext()->GetUiContext()->OpenURL(_newVersionInfo->url);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Cannot open URL: NewVersionInfo for ChangelogWindow is undefined!");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override
|
||||
{
|
||||
const int32_t lineHeight = FontGetLineHeight(FontStyle::Medium);
|
||||
|
||||
ScreenCoordsXY screenCoords(3, 3 - lineHeight);
|
||||
for (const auto& line : _changelogLines)
|
||||
{
|
||||
screenCoords.y += lineHeight;
|
||||
if (screenCoords.y + lineHeight < dpi.y || screenCoords.y >= dpi.y + dpi.height)
|
||||
continue;
|
||||
|
||||
GfxDrawString(dpi, screenCoords, line.c_str(), { colours[0] });
|
||||
}
|
||||
}
|
||||
|
||||
ScreenSize OnScrollGetSize(int32_t scrollIndex) override
|
||||
{
|
||||
return ScreenSize(
|
||||
_changelogLongestLineWidth + 4,
|
||||
static_cast<int32_t>(_changelogLines.size()) * FontGetLineHeight(FontStyle::Medium));
|
||||
}
|
||||
|
||||
// TODO: This probably should be a utility function defined elsewhere for reusability
|
||||
/**
|
||||
* @brief Reimplementation of Window's GetCentrePositionForNewWindow for ChangelogWindow.
|
||||
*
|
||||
* @return ScreenCoordsXY
|
||||
*/
|
||||
static ScreenCoordsXY GetCentrePositionForNewWindow(int32_t width, int32_t height)
|
||||
{
|
||||
auto uiContext = GetContext()->GetUiContext();
|
||||
auto screenWidth = uiContext->GetWidth();
|
||||
auto screenHeight = uiContext->GetHeight();
|
||||
return ScreenCoordsXY{ (screenWidth - width) / 2, std::max(TOP_TOOLBAR_HEIGHT + 1, (screenHeight - height) / 2) };
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Converts NewVersionInfo into changelog lines
|
||||
*
|
||||
*/
|
||||
void NewVersionProcessInfo()
|
||||
{
|
||||
_newVersionInfo = GetContext()->GetNewVersionInfo();
|
||||
if (_newVersionInfo != nullptr)
|
||||
{
|
||||
char version_info[256];
|
||||
|
||||
const char* version_info_ptr = _newVersionInfo->name.c_str();
|
||||
FormatStringLegacy(version_info, 256, STR_NEW_RELEASE_VERSION_INFO, &version_info_ptr);
|
||||
|
||||
_changelogLines.emplace_back(version_info);
|
||||
_changelogLines.emplace_back("");
|
||||
|
||||
ProcessText(_newVersionInfo->changelog);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("ChangelogWindow: Could not process NewVersionInfo, result was undefined");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the absolute path for the changelog file
|
||||
*
|
||||
* @return std::string
|
||||
*/
|
||||
std::string GetChangelogPath()
|
||||
{
|
||||
auto env = GetContext()->GetPlatformEnvironment();
|
||||
return env->GetFilePath(PATHID::CHANGELOG);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Attempts to read the changelog file, returns true on success
|
||||
*
|
||||
*/
|
||||
bool ReadFile(PATHID pathId)
|
||||
{
|
||||
std::string _text;
|
||||
try
|
||||
{
|
||||
_text = GetText(pathId);
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
LOG_ERROR("Unable to allocate memory for text file");
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
LOG_ERROR("Unable to read text file");
|
||||
return false;
|
||||
}
|
||||
|
||||
ProcessText(_text);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void OnOpen() override
|
||||
{
|
||||
widgets = _windowChangelogWidgets;
|
||||
|
||||
WindowInitScrollWidgets(*this);
|
||||
min_width = MIN_WW;
|
||||
min_height = MIN_WH;
|
||||
max_width = MIN_WW;
|
||||
max_height = MIN_WH;
|
||||
}
|
||||
|
||||
void OnResize() override
|
||||
{
|
||||
int32_t screenWidth = ContextGetWidth();
|
||||
int32_t screenHeight = ContextGetHeight();
|
||||
|
||||
max_width = (screenWidth * 4) / 5;
|
||||
max_height = (screenHeight * 4) / 5;
|
||||
|
||||
min_width = MIN_WW;
|
||||
min_height = MIN_WH;
|
||||
|
||||
auto download_button_width = widgets[WIDX_OPEN_URL].width();
|
||||
widgets[WIDX_OPEN_URL].left = (width - download_button_width) / 2;
|
||||
widgets[WIDX_OPEN_URL].right = widgets[WIDX_OPEN_URL].left + download_button_width;
|
||||
|
||||
if (width < min_width)
|
||||
/**
|
||||
* @brief Ingests a string of text and splits it into lines for the changelog and updates the longest line width for
|
||||
* scrolling purposes
|
||||
*
|
||||
* @param text
|
||||
*/
|
||||
void ProcessText(const std::string& text)
|
||||
{
|
||||
Invalidate();
|
||||
width = min_width;
|
||||
}
|
||||
if (height < min_height)
|
||||
{
|
||||
Invalidate();
|
||||
height = min_height;
|
||||
}
|
||||
}
|
||||
std::string::size_type pos = 0;
|
||||
std::string::size_type prev = 0;
|
||||
while ((pos = text.find('\n', prev)) != std::string::npos)
|
||||
{
|
||||
_changelogLines.push_back(text.substr(prev, pos - prev));
|
||||
prev = pos + 1;
|
||||
}
|
||||
|
||||
void OnPrepareDraw() override
|
||||
// To get the last substring (or only, if delimiter is not found)
|
||||
_changelogLines.push_back(text.substr(prev));
|
||||
|
||||
_changelogLongestLineWidth = 0;
|
||||
for (const auto& line : _changelogLines)
|
||||
{
|
||||
int32_t linewidth = GfxGetStringWidth(line.c_str(), FontStyle::Medium);
|
||||
_changelogLongestLineWidth = std::max(linewidth, _changelogLongestLineWidth);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WindowBase* ChangelogOpen(int personality)
|
||||
{
|
||||
ResizeFrameWithPage();
|
||||
widgets[WIDX_SCROLL].right = width - 3;
|
||||
widgets[WIDX_SCROLL].bottom = height - 22;
|
||||
widgets[WIDX_OPEN_URL].bottom = height - 5;
|
||||
widgets[WIDX_OPEN_URL].top = height - 19;
|
||||
}
|
||||
|
||||
void OnMouseUp(WidgetIndex widgetIndex) override
|
||||
{
|
||||
switch (widgetIndex)
|
||||
auto* window = WindowBringToFrontByClass(WindowClass::Changelog);
|
||||
if (window == nullptr)
|
||||
{
|
||||
case WIDX_CLOSE:
|
||||
Close();
|
||||
break;
|
||||
case WIDX_OPEN_URL:
|
||||
if (_newVersionInfo != nullptr)
|
||||
{
|
||||
GetContext()->GetUiContext()->OpenURL(_newVersionInfo->url);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("Cannot open URL: NewVersionInfo for ChangelogWindow is undefined!");
|
||||
}
|
||||
break;
|
||||
// Create a new centred window
|
||||
int32_t screenWidth = ContextGetWidth();
|
||||
int32_t screenHeight = ContextGetHeight();
|
||||
int32_t width = (screenWidth * 4) / 5;
|
||||
int32_t height = (screenHeight * 4) / 5;
|
||||
|
||||
auto pos = ChangelogWindow::GetCentrePositionForNewWindow(width, height);
|
||||
auto* newWindow = WindowCreate<ChangelogWindow>(WindowClass::Changelog, pos, width, height, WF_RESIZABLE);
|
||||
newWindow->SetPersonality(personality);
|
||||
return newWindow;
|
||||
}
|
||||
return window;
|
||||
}
|
||||
|
||||
void OnScrollDraw(int32_t scrollIndex, DrawPixelInfo& dpi) override
|
||||
{
|
||||
const int32_t lineHeight = FontGetLineHeight(FontStyle::Medium);
|
||||
|
||||
ScreenCoordsXY screenCoords(3, 3 - lineHeight);
|
||||
for (const auto& line : _changelogLines)
|
||||
{
|
||||
screenCoords.y += lineHeight;
|
||||
if (screenCoords.y + lineHeight < dpi.y || screenCoords.y >= dpi.y + dpi.height)
|
||||
continue;
|
||||
|
||||
GfxDrawString(dpi, screenCoords, line.c_str(), { colours[0] });
|
||||
}
|
||||
}
|
||||
|
||||
ScreenSize OnScrollGetSize(int32_t scrollIndex) override
|
||||
{
|
||||
return ScreenSize(
|
||||
_changelogLongestLineWidth + 4,
|
||||
static_cast<int32_t>(_changelogLines.size()) * FontGetLineHeight(FontStyle::Medium));
|
||||
}
|
||||
|
||||
// TODO: This probably should be a utility function defined elsewhere for reusability
|
||||
/**
|
||||
* @brief Reimplementation of Window's GetCentrePositionForNewWindow for ChangelogWindow.
|
||||
*
|
||||
* @return ScreenCoordsXY
|
||||
*/
|
||||
static ScreenCoordsXY GetCentrePositionForNewWindow(int32_t width, int32_t height)
|
||||
{
|
||||
auto uiContext = GetContext()->GetUiContext();
|
||||
auto screenWidth = uiContext->GetWidth();
|
||||
auto screenHeight = uiContext->GetHeight();
|
||||
return ScreenCoordsXY{ (screenWidth - width) / 2, std::max(TOP_TOOLBAR_HEIGHT + 1, (screenHeight - height) / 2) };
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Converts NewVersionInfo into changelog lines
|
||||
*
|
||||
*/
|
||||
void NewVersionProcessInfo()
|
||||
{
|
||||
_newVersionInfo = GetContext()->GetNewVersionInfo();
|
||||
if (_newVersionInfo != nullptr)
|
||||
{
|
||||
char version_info[256];
|
||||
|
||||
const char* version_info_ptr = _newVersionInfo->name.c_str();
|
||||
FormatStringLegacy(version_info, 256, STR_NEW_RELEASE_VERSION_INFO, &version_info_ptr);
|
||||
|
||||
_changelogLines.emplace_back(version_info);
|
||||
_changelogLines.emplace_back("");
|
||||
|
||||
ProcessText(_newVersionInfo->changelog);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("ChangelogWindow: Could not process NewVersionInfo, result was undefined");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the absolute path for the changelog file
|
||||
*
|
||||
* @return std::string
|
||||
*/
|
||||
std::string GetChangelogPath()
|
||||
{
|
||||
auto env = GetContext()->GetPlatformEnvironment();
|
||||
return env->GetFilePath(PATHID::CHANGELOG);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Attempts to read the changelog file, returns true on success
|
||||
*
|
||||
*/
|
||||
bool ReadFile(PATHID pathId)
|
||||
{
|
||||
std::string _text;
|
||||
try
|
||||
{
|
||||
_text = GetText(pathId);
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
LOG_ERROR("Unable to allocate memory for text file");
|
||||
return false;
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
LOG_ERROR("Unable to read text file");
|
||||
return false;
|
||||
}
|
||||
|
||||
ProcessText(_text);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ingests a string of text and splits it into lines for the changelog and updates the longest line width for
|
||||
* scrolling purposes
|
||||
*
|
||||
* @param text
|
||||
*/
|
||||
void ProcessText(const std::string& text)
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
std::string::size_type prev = 0;
|
||||
while ((pos = text.find('\n', prev)) != std::string::npos)
|
||||
{
|
||||
_changelogLines.push_back(text.substr(prev, pos - prev));
|
||||
prev = pos + 1;
|
||||
}
|
||||
|
||||
// To get the last substring (or only, if delimiter is not found)
|
||||
_changelogLines.push_back(text.substr(prev));
|
||||
|
||||
_changelogLongestLineWidth = 0;
|
||||
for (const auto& line : _changelogLines)
|
||||
{
|
||||
int32_t linewidth = GfxGetStringWidth(line.c_str(), FontStyle::Medium);
|
||||
_changelogLongestLineWidth = std::max(linewidth, _changelogLongestLineWidth);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WindowBase* WindowChangelogOpen(int personality)
|
||||
{
|
||||
auto* window = WindowBringToFrontByClass(WindowClass::Changelog);
|
||||
if (window == nullptr)
|
||||
{
|
||||
// Create a new centred window
|
||||
int32_t screenWidth = ContextGetWidth();
|
||||
int32_t screenHeight = ContextGetHeight();
|
||||
int32_t width = (screenWidth * 4) / 5;
|
||||
int32_t height = (screenHeight * 4) / 5;
|
||||
|
||||
auto pos = ChangelogWindow::GetCentrePositionForNewWindow(width, height);
|
||||
auto* newWindow = WindowCreate<ChangelogWindow>(WindowClass::Changelog, pos, width, height, WF_RESIZABLE);
|
||||
newWindow->SetPersonality(personality);
|
||||
return newWindow;
|
||||
}
|
||||
return window;
|
||||
}
|
||||
} // namespace OpenRCT2::Ui::Windows
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue