Merge branch 'develop'

This commit is contained in:
Michał Janiszewski 2024-04-02 22:14:07 +02:00
commit 87fe784da0
498 changed files with 99434 additions and 70321 deletions

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -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 dalmenys 600.
STR_2386 :{BLACK}Tenir almenys {COMMA32} visitants al parc al final de {MONTHYEAR}, amb una valoració del parc dalmenys 600.
STR_2387 :{BLACK}Aconseguir una valoració del parc dalmenys {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 delles amb un valor democió dalmenys 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 datraccions dalmenys {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 democió dalmenys 7,00.
STR_2394 :{BLACK}Acabar de construir les cinc muntanyes russes que shavien començat a construir al parc, dissenyant-les per a aconseguir un nivell democió dalmenys {POP16}{POP16}{COMMA2DP32} per cada una delles.
@ -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 democió:
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 :Lelement de camí no sha trobat
STR_6611 :Lelement de mur no sha trobat
STR_6612 :Lelement de text de cartell no sha trobat
STR_6613 :Recarrega lobjecte
STR_6614 :El preu de lentrada al parc no es pot canviar
STR_6615 :La via d'aquesta casella necessita aigua
STR_6616 :Lacció no és vàlida per a aquest tipus dempleat
STR_6617 :No es pot intercanviar lelement de la casella amb si mateix
#############
# Scenarios #

View File

@ -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:

View File

@ -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:

View File

@ -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 #

View File

@ -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 Gs, 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 #

View File

@ -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!

View File

@ -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 #

View File

@ -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 #

View File

@ -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:

View File

@ -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 dau moins 600
STR_2386 :{BLACK}Avoir au moins {COMMA32} visiteurs dans votre parc à la fin {MONTHYEAR}, avec une évaluation de parc dau moins 600
STR_2387 :{BLACK}Atteindre une valeur de parc dau 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 dentre elles atteignant un indice dengouement dau 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 dattraction dau moins {POP16}{POP16}{CURRENCY}
STR_2393 :{BLACK}Avoir construit au moins 10 montagnes russes différentes dans votre parc, chacune dentre elles atteignant une longueur dau moins {LENGTH}, avec un indice dengouement dau moins 7.00
STR_2394 :{BLACK}Terminer la construction des 5 montagnes russes entamées dans ce parc, avec une conception leur permettant datteindre un indice dengouement dau 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 dengouement :
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 lobjet
STR_6614 :Impossible de changer le prix dentrée du parc
STR_6615 :La voie a besoin deau 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 lobjet…
STR_6619 :Ce type dobjet ne peut pas être restreint!
STR_6620 :Objet introuvable!
STR_6621 :Restreindre
STR_6622 :Restreint lobjet à léditeur de scénario et au mode bac à sable
#############
# Scenarios #

View File

@ -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

View File

@ -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:

View File

@ -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}保全命令のライド:

View File

@ -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 #

View File

@ -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 #

View File

@ -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 #

View File

@ -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:

View File

@ -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 #

View File

@ -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}Аттракционы в зарезервированном порядке:

View File

@ -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:

View File

@ -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 700den aşağı düşmemesi gerekli
STR_2391 :{BLACK}Parkınızda en az {COMMA32} müşteri bulunması ve park değerlendirmesinin hiç bir zaman 700den 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

View File

@ -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

View File

@ -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:

View File

@ -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}请选取要保留的游乐设施/店铺:

View File

@ -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}請選取要保留的遊樂設施/店鋪:

View File

@ -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 Citys scenario description incorrectly states there are height restrictions.

View File

@ -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" |

View File

@ -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

View File

@ -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')
}

View File

@ -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/../")

View File

@ -80,6 +80,8 @@ public class GameActivity extends SDLActivity {
return new String[]{
"c++_shared",
"speexdsp",
"bz2",
"freetype",
"z",
"png16",
"SDL2",

View File

@ -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;
}
}
}

View File

@ -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

View File

@ -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

View File

@ -11,6 +11,8 @@
#include <SDL.h>
using namespace OpenRCT2::Ui;
SDLException::SDLException(const std::string& message)
: runtime_error(message.c_str())
{

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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 })
{

View File

@ -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>();
}

View File

@ -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

View File

@ -9,4 +9,7 @@
#pragma once
void RegisterBitmapReader();
namespace OpenRCT2::Ui
{
void RegisterBitmapReader();
}

View File

@ -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

View File

@ -11,6 +11,8 @@
# include "ApplyPaletteShader.h"
using namespace OpenRCT2::Ui;
namespace
{
struct VDStruct

View File

@ -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

View File

@ -11,6 +11,8 @@
# include "ApplyTransparencyShader.h"
using namespace OpenRCT2::Ui;
namespace
{
struct VDStruct

View File

@ -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

View File

@ -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

View File

@ -13,6 +13,8 @@
# include "OpenGLFramebuffer.h"
using namespace OpenRCT2::Ui;
namespace
{
struct VDStruct

View File

@ -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

View File

@ -11,6 +11,8 @@
# include "DrawRectShader.h"
using namespace OpenRCT2::Ui;
namespace
{
struct VDStruct

View File

@ -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

View File

@ -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

View File

@ -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)
{

View File

@ -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

View File

@ -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 */

View File

@ -16,6 +16,8 @@
# include <memory>
# include <openrct2/common.h>
using namespace OpenRCT2::Ui;
constexpr GLuint BACKBUFFER_ID = 0;
OpenGLFramebuffer::OpenGLFramebuffer(SDL_Window* window)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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 };

View File

@ -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

View File

@ -19,6 +19,8 @@
# include <stdexcept>
# include <vector>
using namespace OpenRCT2::Ui;
constexpr uint32_t UNUSED_INDEX = 0xFFFFFFFF;
TextureCache::TextureCache()

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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";

View File

@ -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));

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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;

View File

@ -22,6 +22,7 @@
#include <openrct2/world/Surface.h>
using namespace OpenRCT2;
using namespace OpenRCT2::Ui::Windows;
// clang-format off
static uint16_t toolSizeSpriteIndices[] =

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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)

View File

@ -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

View File

@ -0,0 +1,6 @@
#pragma once
namespace OpenRCT2::Audio
{
void UpdateVehicleSounds();
}

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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);
});

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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