Merge pull request #7310 from OpenRCT2/json-objects

Add support for new JSON object format
This commit is contained in:
Duncan 2018-04-08 10:45:55 +01:00 committed by GitHub
commit 0ab3d0955c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
60 changed files with 2192 additions and 692 deletions

11
.vscode/launch.json vendored
View File

@ -1,19 +1,22 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "C++ Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceRoot}/bin/openrct2",
"program": "${workspaceFolder}/bin/openrct2",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceRoot}/bin",
"cwd": "${workspaceFolder}/bin",
"environment": [],
"externalConsole": true,
"setupCommands": [
{
"text": "-enable-pretty-printing"
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"linux": {
@ -30,7 +33,7 @@
"name": "C++ Attach",
"type": "cppdbg",
"request": "attach",
"program": "${workspaceRoot}/bin/openrct2",
"program": "${workspaceFolder}/bin/openrct2",
"processId": "${command:pickProcess}",
"setupCommands": [
{

View File

@ -16,10 +16,14 @@ set(CMAKE_MACOSX_RPATH 1)
set(TITLE_SEQUENCE_URL "https://github.com/OpenRCT2/title-sequences/releases/download/v0.1.2/title-sequence-v0.1.2.zip")
set(TITLE_SEQUENCE_SHA1 "1136ef92bfb05cd1cba9831ba6dc4a653d87a246")
set(OBJECTS_URL "https://github.com/OpenRCT2/objects/releases/download/v1.0/objects.zip")
set(OBJECTS_SHA1 "4f670859b5f37f85e0c3d755b1cb75e839c989a6")
option(FORCE32 "Force 32-bit build. It will add `-m32` to compiler flags.")
option(WITH_TESTS "Build tests")
option(PORTABLE "Create a portable build (-rpath=$ORIGIN)" OFF)
option(DOWNLOAD_TITLE_SEQUENCES "Download title sequences during installation." ON)
option(DOWNLOAD_OBJECTS "Download objects during installation." ON)
# Options
option(STATIC "Create a static build.")
@ -208,6 +212,17 @@ if (DOWNLOAD_TITLE_SEQUENCES)
file(REMOVE \$ENV{DESTDIR}${CMAKE_INSTALL_FULL_DATADIR}/${PROJECT}/title/title-sequences.zip)\n\
endif ()")
endif ()
if (DOWNLOAD_OBJECTS)
# If rct2.wtrcyan.json or data/object/ exists, assume all the objects are already present
install(CODE
"if (EXISTS \"\$ENV{DESTDIR}/${CMAKE_INSTALL_FULL_DATADIR}/${PROJECT}/object/rct2/water/rct2.wtrcyan.json\" OR EXISTS ${CMAKE_SOURCE_DIR}/data/object/)\n\
message(\"Using cached objects\")\n\
else () \n\
file(DOWNLOAD ${OBJECTS_URL} \$ENV{DESTDIR}/${CMAKE_INSTALL_FULL_DATADIR}/${PROJECT}/object/objects.zip EXPECTED_HASH SHA1=${OBJECTS_SHA1} SHOW_PROGRESS)\n\
execute_process(COMMAND \"${CMAKE_COMMAND}\" -E chdir \$ENV{DESTDIR}/${CMAKE_INSTALL_FULL_DATADIR}/${PROJECT}/object/ \"${CMAKE_COMMAND}\" -E tar xvf objects.zip)\n\
file(REMOVE \$ENV{DESTDIR}/${CMAKE_INSTALL_FULL_DATADIR}/${PROJECT}/object/objects.zip)\n\
endif ()")
endif ()
install(TARGETS "libopenrct2" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}")
install(TARGETS "openrct2" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")

View File

@ -21,10 +21,13 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
4C1A53ED205FD1A0000F8EF5 /* SceneryObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C1A53EC205FD19F000F8EF5 /* SceneryObject.cpp */; };
4C3B4236205914F7000C5BB7 /* InGameConsole.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C3B4234205914F7000C5BB7 /* InGameConsole.cpp */; };
4C3B423820591513000C5BB7 /* StdInOutConsole.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C3B423720591513000C5BB7 /* StdInOutConsole.cpp */; };
4C93F1AD1F8CD9F000A9330D /* Input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C93F1AC1F8CD9F000A9330D /* Input.cpp */; };
4C93F1AF1F8CD9F600A9330D /* KeyboardShortcut.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C93F1AE1F8CD9F600A9330D /* KeyboardShortcut.cpp */; };
4CE9AAAD1FDA7B14004093C6 /* ObjectJsonHelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CE9AAAB1FDA7B14004093C6 /* ObjectJsonHelpers.cpp */; };
4CF67197206B7E720034ADDD /* object in Resources */ = {isa = PBXBuildFile; fileRef = 4CF67196206B7E720034ADDD /* object */; };
C61ADB1F1FB6A0A70024F2EF /* TopToolbar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C61ADB1E1FB6A0A60024F2EF /* TopToolbar.cpp */; };
C61ADB211FB7DC060024F2EF /* Scenery.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C61ADB201FB7DC060024F2EF /* Scenery.cpp */; };
C61ADB231FBBCB8B0024F2EF /* GameBottomToolbar.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C61ADB221FBBCB8A0024F2EF /* GameBottomToolbar.cpp */; };
@ -586,6 +589,7 @@
/* Begin PBXFileReference section */
4C04D69F2056AA9600F82EBA /* linenoise.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = linenoise.hpp; sourceTree = "<group>"; };
4C1A53EC205FD19F000F8EF5 /* SceneryObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SceneryObject.cpp; sourceTree = "<group>"; };
4C3B4234205914F7000C5BB7 /* InGameConsole.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InGameConsole.cpp; sourceTree = "<group>"; };
4C3B4235205914F7000C5BB7 /* InGameConsole.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InGameConsole.h; sourceTree = "<group>"; };
4C3B423720591513000C5BB7 /* StdInOutConsole.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StdInOutConsole.cpp; sourceTree = "<group>"; };
@ -847,6 +851,9 @@
4CE462461FD1613D0001CD98 /* Platform.Linux.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Platform.Linux.cpp; sourceTree = "<group>"; };
4CE462481FD1613D0001CD98 /* Platform.Posix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Platform.Posix.cpp; sourceTree = "<group>"; };
4CE462491FD1613D0001CD98 /* Platform.Win32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Platform.Win32.cpp; sourceTree = "<group>"; };
4CE9AAAB1FDA7B14004093C6 /* ObjectJsonHelpers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjectJsonHelpers.cpp; sourceTree = "<group>"; };
4CE9AAAC1FDA7B14004093C6 /* ObjectJsonHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectJsonHelpers.h; sourceTree = "<group>"; };
4CF67196206B7E720034ADDD /* object */ = {isa = PBXFileReference; lastKnownFileType = folder; name = object; path = data/object; sourceTree = "<group>"; };
4CFE4E7B1F90A3F1005243C2 /* Peep.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Peep.cpp; sourceTree = "<group>"; };
4CFE4E7C1F90A3F1005243C2 /* Peep.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Peep.h; sourceTree = "<group>"; };
4CFE4E7D1F90A3F1005243C2 /* PeepData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PeepData.cpp; sourceTree = "<group>"; };
@ -1676,6 +1683,7 @@
D497D06F1C20FD52002BF46A = {
isa = PBXGroup;
children = (
4CF67196206B7E720034ADDD /* object */,
D41B72431C21015A0080A7B9 /* Sources */,
D497D07A1C20FD52002BF46A /* Resources */,
D41B73ED1C21017D0080A7B9 /* Libraries */,
@ -2083,6 +2091,8 @@
4C7B53A21FFC15ED00A52E21 /* ObjectLimits.h */,
4C7B53A31FFC180400A52E21 /* ObjectList.cpp */,
4C7B53A41FFC180400A52E21 /* ObjectList.h */,
4CE9AAAB1FDA7B14004093C6 /* ObjectJsonHelpers.cpp */,
4CE9AAAC1FDA7B14004093C6 /* ObjectJsonHelpers.h */,
F76C84221EC4E7CC00FA49E2 /* ObjectManager.cpp */,
F76C84231EC4E7CC00FA49E2 /* ObjectManager.h */,
F76C84241EC4E7CC00FA49E2 /* ObjectRepository.cpp */,
@ -2091,6 +2101,7 @@
F76C84271EC4E7CC00FA49E2 /* RideObject.h */,
F76C84281EC4E7CC00FA49E2 /* SceneryGroupObject.cpp */,
F76C84291EC4E7CC00FA49E2 /* SceneryGroupObject.h */,
4C1A53EC205FD19F000F8EF5 /* SceneryObject.cpp */,
F76C842A1EC4E7CC00FA49E2 /* SceneryObject.h */,
F76C842B1EC4E7CC00FA49E2 /* SmallSceneryObject.cpp */,
F76C842C1EC4E7CC00FA49E2 /* SmallSceneryObject.h */,
@ -2717,6 +2728,7 @@
isa = PBXNativeTarget;
buildConfigurationList = D497D0891C20FD53002BF46A /* Build configuration list for PBXNativeTarget "OpenRCT2" */;
buildPhases = (
4CF67195206B7BEF0034ADDD /* Download JSON objects */,
D4E09E831E049C0600F53CE3 /* Download Title Sequences */,
D4EC012A1C25532B00DAFE69 /* Setup AppIcon */,
D4CA88671D4E962100060C11 /* Get Git Variables */,
@ -2828,6 +2840,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
4CF67197206B7E720034ADDD /* object in Resources */,
D41B74731C2125E50080A7B9 /* Assets.xcassets in Resources */,
D4EC48E61C2637710024B507 /* g2.dat in Resources */,
D4EC48E71C2637710024B507 /* language in Resources */,
@ -2847,6 +2860,20 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
4CF67195206B7BEF0034ADDD /* Download JSON objects */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Download JSON objects";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "version=\"1.0\"\nzipname=\"objects.zip\"\nliburl=\"https://github.com/OpenRCT2/objects/releases/download/v$version/$zipname\"\n\n[[ ! -d \"${SRCROOT}/data/object\" || ! -e \"${SRCROOT}/objectsversion\" || $(head -n 1 \"${SRCROOT}/objectsversion\") != $version ]]\noutdated=$?\n\nif [[ $outdated -eq 0 ]]; then\nif [[ -d \"${SRCROOT}/data/object\" ]]; then rm -r \"${SRCROOT}/data/object\"; fi\nmkdir -p \"${SRCROOT}/data/object\"\n\ncurl -L -o \"${SRCROOT}/data/object/$zipname\" \"$liburl\"\nunzip -uaq -d \"${SRCROOT}/data/object\" \"${SRCROOT}/data/object/$zipname\"\nrm \"${SRCROOT}/data/object/$zipname\"\n\necho $version > \"${SRCROOT}/objectsversion\"\nfi";
};
C68B2D471EC790710020651C /* Download Libraries */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -3003,6 +3030,7 @@
C68313C81FDB4ED4006DB3D8 /* MouseInput.cpp in Sources */,
C68878C920289B710084B384 /* TextureCache.cpp in Sources */,
C61ADB1F1FB6A0A70024F2EF /* TopToolbar.cpp in Sources */,
4C1A53ED205FD1A0000F8EF5 /* SceneryObject.cpp in Sources */,
F76C887B1EC5324E00FA49E2 /* FileAudioSource.cpp in Sources */,
C68878CA20289B710084B384 /* TransparencyDepth.cpp in Sources */,
C64644FD1F3FA4120026AC2D /* Land.cpp in Sources */,
@ -3043,6 +3071,7 @@
C68313D51FDB4F4C006DB3D8 /* Graph.cpp in Sources */,
C685E51D1F8907850090598F /* Research.cpp in Sources */,
C64644FB1F3FA4120026AC2D /* EditorScenarioOptions.cpp in Sources */,
4CE9AAAD1FDA7B14004093C6 /* ObjectJsonHelpers.cpp in Sources */,
C654DF321F69C0430040F43D /* InstallTrack.cpp in Sources */,
C64644FF1F3FA4120026AC2D /* StaffList.cpp in Sources */,
C6D2BEE81F9BAACE008B557C /* MazeConstruction.cpp in Sources */,

View File

@ -1600,7 +1600,7 @@ STR_1594 :{SMALLFONT}{OPENQUOTES}This wonton soup from {STRINGID} is really g
STR_1595 :{SMALLFONT}{OPENQUOTES}This meatball soup from {STRINGID} is really good value{ENDQUOTES}
STR_1596 :{SMALLFONT}{OPENQUOTES}This fruit juice from {STRINGID} is really good value{ENDQUOTES}
STR_1597 :{SMALLFONT}{OPENQUOTES}This soybean milk from {STRINGID} is really good value{ENDQUOTES}
STR_1598 :{SMALLFONT}{OPENQUOTES}This sujongkwa from {STRINGID} is really good value{ENDQUOTES}
STR_1598 :{SMALLFONT}{OPENQUOTES}This sujeonggwa from {STRINGID} is really good value{ENDQUOTES}
STR_1599 :{SMALLFONT}{OPENQUOTES}This sub sandwich from {STRINGID} is really good value{ENDQUOTES}
STR_1600 :{SMALLFONT}{OPENQUOTES}This cookie from {STRINGID} is really good value{ENDQUOTES}
STR_1601 :
@ -1632,7 +1632,7 @@ STR_1626 :{SMALLFONT}{OPENQUOTES}I'm not paying that much for wonton soup fro
STR_1627 :{SMALLFONT}{OPENQUOTES}I'm not paying that much for meatball soup from {STRINGID}{ENDQUOTES}
STR_1628 :{SMALLFONT}{OPENQUOTES}I'm not paying that much for fruit juice from {STRINGID}{ENDQUOTES}
STR_1629 :{SMALLFONT}{OPENQUOTES}I'm not paying that much for soybean milk from {STRINGID}{ENDQUOTES}
STR_1630 :{SMALLFONT}{OPENQUOTES}I'm not paying that much for sujongkwa from {STRINGID}{ENDQUOTES}
STR_1630 :{SMALLFONT}{OPENQUOTES}I'm not paying that much for sujeonggwa from {STRINGID}{ENDQUOTES}
STR_1631 :{SMALLFONT}{OPENQUOTES}I'm not paying that much for a sub sandwich from {STRINGID}{ENDQUOTES}
STR_1632 :{SMALLFONT}{OPENQUOTES}I'm not paying that much for a cookie from {STRINGID}{ENDQUOTES}
STR_1633 :
@ -2118,7 +2118,7 @@ STR_2110 :{WINDOW_COLOUR_2}Wonton Soup price:
STR_2111 :{WINDOW_COLOUR_2}Meatball Soup price:
STR_2112 :{WINDOW_COLOUR_2}Fruit Juice price:
STR_2113 :{WINDOW_COLOUR_2}Soybean Milk price:
STR_2114 :{WINDOW_COLOUR_2}Sujongkwa price:
STR_2114 :{WINDOW_COLOUR_2}Sujeonggwa price:
STR_2115 :{WINDOW_COLOUR_2}Sub Sandwich price:
STR_2116 :{WINDOW_COLOUR_2}Cookie price:
STR_2117 :{WINDOW_COLOUR_2}
@ -2140,7 +2140,7 @@ STR_2132 :Wonton Soup
STR_2133 :Meatball Soup
STR_2134 :Fruit Juice
STR_2135 :Soybean Milk
STR_2136 :Sujongkwa
STR_2136 :Sujeonggwa
STR_2137 :Sub Sandwich
STR_2138 :Cookie
STR_2139 :Empty Bowl
@ -2162,7 +2162,7 @@ STR_2154 :Wonton Soups
STR_2155 :Meatball Soups
STR_2156 :Fruit Juices
STR_2157 :Soybean Milks
STR_2158 :Sujongkwa
STR_2158 :Sujeonggwa
STR_2159 :Sub Sandwiches
STR_2160 :Cookies
STR_2161 :Empty Bowls
@ -2184,7 +2184,7 @@ STR_2176 :some Wonton Soup
STR_2177 :some Meatball Soup
STR_2178 :a Fruit Juice
STR_2179 :some Soybean Milk
STR_2180 :some Sujongkwa
STR_2180 :some Sujeonggwa
STR_2181 :a Sub Sandwich
STR_2182 :a Cookie
STR_2183 :an Empty Bowl
@ -2206,7 +2206,7 @@ STR_2198 :Wonton Soup
STR_2199 :Meatball Soup
STR_2200 :Fruit Juice
STR_2201 :Soybean Milk
STR_2202 :Sujongkwa
STR_2202 :Sujeonggwa
STR_2203 :Sub Sandwich
STR_2204 :Cookie
STR_2205 :Empty Bowl
@ -4988,411 +4988,10 @@ STR_SCNR :Fort Anachronism
STR_PARK :Fort Anachronism
STR_DTLS :
#####################
# Rides/attractions #
#####################
#RCT2
[TOPSP1]
STR_NAME :Top Spin
STR_DESC :Passengers ride in a gondola suspended by large rotating arms, rotating forwards and backwards head-over-heels
STR_CPTY :8 passengers
[BMSD]
STR_NAME :Twister Trains
STR_DESC :A spacious train with shoulder restraints
[BMSU]
STR_NAME :Stand-up Twister Trains
STR_DESC :A train with shoulder restraints, in which the riders stand up
[BMFL]
STR_NAME :Floorless Twister Trains
STR_DESC :A spacious train with shoulder restraints and no floor, making for a more exciting ride
[WMMINE]
STR_NAME :Mine Cars
STR_DESC :Cars shaped like an old mine cart
[WMOUSE]
STR_NAME :Mouse Cars
STR_DESC :Indivual cars shaped like a mouse
[STEEP1]
STR_NAME :Horses
STR_DESC :Single cars shaped like a horse
[STEEP2]
STR_NAME :Motorbikes
STR_DESC :Single cars shaped like a motorbike
[SBOX]
STR_NAME :Soap boxes
STR_DESC :Single cars shaped like a soap box
[BOB1]
STR_NAME :Bobsleigh Trains
STR_DESC :A train consisting of 2-seater cars where the riders are behind each other
[INTBOB]
STR_NAME :6-seater Bobsleighs
STR_DESC :Bobsleighs with three seating rows, with room for two people on each
[LIFT1]
STR_NAME :Lift Cabin
STR_DESC :Steel lift cabin
[ARRT1]
STR_NAME :Corkscrew Roller Coaster Trains
STR_DESC :Roller coaster train with shoulder restraints
[ARRT2]
STR_NAME :Hypercoaster Trains
STR_DESC :Comfortable trains with only lap bar restraints
[MONBK]
STR_NAME :Bicycles
[OBS1]
STR_NAME :Single-deck Cabin
[OBS2]
STR_NAME :Double-deck Cabin
[GTC]
STR_NAME :Ghost Train Cars
[HMCAR]
STR_NAME :Haunted Mansion Cars
[VREEL]
STR_NAME :Virginia Reel tubs
[WMSPIN]
STR_NAME :Spinning Mouse Cars
[AMT1]
STR_NAME :Mine Trains
[SLCT]
STR_NAME :Compact Inverted Coaster Trains
[SLCFO]
STR_NAME :Face-off Cars
[VEKDV]
STR_NAME :Vertical Shuttle Cars
[THCAR]
STR_NAME :Air Powered Vertical Coaster Trains
[SSC1]
STR_NAME :Launched Freefall car
[DING1]
STR_NAME :Dinghies
[LFB1]
STR_NAME :Logs
[RFTBOAT]
STR_NAME :Rafts
[IVMC1]
STR_NAME :Four-seater Cars
[SPDRCR]
STR_NAME :Spiral Roller Coaster Trains
[TOGST]
STR_NAME :Stand-up Roller Coaster Trains
[PREMT1]
STR_NAME :LIM Launched Roller Coaster Trains
[PMT1]
STR_NAME :Powered mine train
[BMAIR]
STR_NAME :Flying Roller Coaster Trains
[INTINV]
STR_NAME :Impulse Trains
[INTST]
STR_NAME :Giga Coaster Trains
[NEMT]
STR_NAME :4-across Inverted Roller Coaster Trains
[VEKST]
STR_NAME :Lay-down Roller Coaster Trains
[REVCAR]
STR_NAME :Reverser Cars
STR_DESC :Bogied cars capable of turning around on special reversing sections
[SKYTR]
STR_NAME :Lay-down Cars
[BMVD]
STR_NAME :Six-seater Twister Trains
[SUBMAR]
STR_NAME :Submarines
#WW
[CONDORRD]
STR_NAME :Condor Trains
STR_DESC :Riding in special harnesses below the track, riders experience the feeling of flight as they swoop through the air in Condor-shaped trains
STR_CPTY :4 passengers per car
#WW
[CONGAEEL]
STR_NAME :Conger Eel Trains
STR_DESC :Trains with shoulder restraints, in the shape of a Conger Eel.
#WW
[MINELIFT]
STR_NAME :Mine Lift Cabin
STR_DESC :A steel lift cabin commonly used in mines
#WW
[ANACONDA]
STR_NAME :Anaconda Trains
#WW
[CROCFLUM]
STR_NAME :Crocodile boats
#WW
[WHICGRUB]
STR_NAME :Witchity Grub Trains
#WW
[GRATWHTE]
STR_NAME :Great White Shark Trains
STR_DESC :Trains with shoulder restraints, in the shape of a Great White Shark
#WW
[BOMERANG]
STR_NAME :Boomerang Trains
#WW
[KOLARIDE]
STR_NAME :Koala car
#WW
[MANTARAY]
STR_NAME :Manta Ray boats
STR_DESC :Coaster boats in the shape of a Manta Ray
#WW
[TUTLBOAT]
STR_NAME :Turtle boats
#WW, for the Wooden RC
[MINECART]
STR_NAME :Mine Cart Trains
#WW
[LIONRIDE]
STR_NAME :Lion Cars
#WW
[RHINORID]
STR_NAME :Rhino Trains
#WW
[OSTRICH]
STR_NAME :Ostrich Trains
#WW
[GORILLA]
STR_NAME :Gorilla Trains
#WW
[FOOTBALL]
STR_NAME :Football Trains
#WW
[TIGRTWST]
STR_NAME :Bengal Tiger Cars
#WW
[TAXICSTR]
STR_NAME :Yellow Taxi Trains
#WW
[OUTRIGGR]
STR_NAME :Outrigger canoes
#WW
[SANFTRAM]
STR_NAME :San Francisco Trams
#WW
[PENGUINB]
STR_NAME :Penguin Trains
#WW
[POLARBER]
STR_NAME :Polar Bear Trains
#WW
[LONDONBS]
STR_NAME :Routemaster buses
STR_DESC :Replicas of the famous London Routemaster bus
#WW
[BLACKCAB]
STR_NAME :Black Cabs
#WW
[TGVTRAIN]
STR_NAME :TGV Trains
#WW
[ROCKET]
STR_NAME :1950's Rockets
#WW
[SPUTNIKR]
STR_NAME :Sputnik Cars
#WW
[DHOWWATR]
STR_NAME :Dhow boats
#WW
[SURFBRDC]
STR_NAME :Surfing Trains
#WW
[KILLWHAL]
STR_NAME :Killer Whale Submarines
#WW
[HIPPORID]
STR_NAME :Hippo Submarines
#WW
[DOLPHINR]
STR_NAME :Dolphin boats
#WW
[MANDARIN]
STR_NAME :Mandarin Duck Boats
STR_DESC :Duck shaped boat, propelled by the pedalling front seat passengers
#TT
[BATTRRAM]
STR_NAME :Battering Ram Trains
#TT
[BLCKDETH]
STR_NAME :Black Death Trains
#TT
[JOUSTING]
STR_NAME :Jousting Knights
#TT
[OAKBAREL]
STR_NAME :Oak Barrels
#TT
[STAMPHRD]
STR_NAME :Stampeding Herd Trains
#TT
[DRAGNFLY]
STR_NAME :Dragonfly Cars
#TT
[PTERODAC]
STR_NAME :Pterodactyl Trains
#TT
[PEGASUSX]
STR_NAME :Pegasus Cars
#TT
[CERBERUS]
STR_NAME :Cerberus Trains
#TT
[HARPIESX]
STR_NAME :Harpies Trains
#TT
[VALKYRIE]
STR_NAME :Valkyries Trains
#TT
[RIVRSTYX]
STR_NAME :River Styx boats
#TT
[TELEPTER]
STR_NAME :Teleporter Cabin
#TT
[HOVERCAR]
STR_NAME :Hover Cars
#TT
[HOVRBORD]
STR_NAME :Hoverboards
#TT
[HOVERBKE]
STR_NAME :Hover Bikes
#TT, Reverser RC
[POLICECR]
STR_NAME :Police Cars
#TT, Looping RC
[POLCHASE]
STR_NAME :Police Car Trains
#TT
[GANSTRCR]
STR_NAME :Gangster Cars
#TT
[SEAPLANE]
STR_NAME :Suspended Seaplane Cars
#TT
[BARNSTRM]
STR_NAME :BarnStorming Cars
#TT
[FLYGBOAT]
STR_NAME :Flying boats
#TT
[BMVOCTPS]
STR_NAME :Blob from Outer Space
#TT
[JETPLANE]
STR_NAME :Jet Plane Cars
#TT
[HOTRODXX]
STR_NAME :Hot Rod Cars
###########
# Scenery #
###########
#Bulrushes was spelt incorrectly
[TBR]
STR_NAME :Bulrushes
## Start OpenRCT2 Official
[XXBBBR01]
STR_NAME :Base Block

View File

@ -71,6 +71,8 @@
<GtestSha1>667f873ab7a4d246062565fad32fb6d8e203ee73</GtestSha1>
<TitleSequencesUrl>https://github.com/OpenRCT2/title-sequences/releases/download/v0.1.2/title-sequence-v0.1.2.zip</TitleSequencesUrl>
<TitleSequencesSha1>1136ef92bfb05cd1cba9831ba6dc4a653d87a246</TitleSequencesSha1>
<ObjectsUrl>https://github.com/OpenRCT2/objects/releases/download/v1.0/objects.zip</ObjectsUrl>
<ObjectsSha1>4f670859b5f37f85e0c3d755b1cb75e839c989a6</ObjectsSha1>
</PropertyGroup>
<ItemGroup>
@ -214,6 +216,15 @@
OutputDirectory="$(TargetDir)data\title" />
</Target>
<!-- Target to download the objects -->
<Target Name="DownloadObjects" AfterTargets="Build">
<DownloadDependency Name="Objects"
Url="$(ObjectsUrl)"
Sha1="$(ObjectsSha1)"
CheckFile="$(DependenciesCheckFile)"
OutputDirectory="$(TargetDir)data\object" />
</Target>
<!-- Target to sign OpenRCT2
This requires the project parameter SIGN_PASSWORD to be set -->
<Target Name="Sign" AfterTargets="Build" Inputs="@(SignItems)" Outputs="%(SignItems.Identity).signed"

View File

@ -90,6 +90,14 @@ android.applicationVariants.all { variant ->
from zipTree(new File(buildDir, 'title-sequence.zip'))
into "$variant.mergeAssets.outputDir/data/title"
}
download {
src 'https://github.com/OpenRCT2/objects/releases/download/v1.0/objects.zip'
dest new File(buildDir, 'objects.zip')
}
copy {
from zipTree(new File(buildDir, 'objects.zip'))
into "$variant.mergeAssets.outputDir/data/object"
}
}
}

View File

@ -21,6 +21,7 @@
#include <openrct2/audio/audio.h>
#include <openrct2/config/Config.h>
#include <openrct2/Context.h>
#include <openrct2/core/String.hpp>
#include <openrct2/Editor.h>
#include <openrct2/EditorObjectSelectionSession.h>
#include <openrct2/Game.h>
@ -304,7 +305,7 @@ static bool visible_list_sort_ride_type(const list_item &a, const list_item &b)
{
auto rideTypeA = language_get_string(get_ride_type_string_id(a.repositoryItem));
auto rideTypeB = language_get_string(get_ride_type_string_id(b.repositoryItem));
sint32 result = strcmp(rideTypeA, rideTypeB);
sint32 result = String::Compare(rideTypeA, rideTypeB);
return result != 0 ?
result < 0 :
visible_list_sort_ride_name(a, b);

View File

@ -3370,7 +3370,6 @@ static void window_ride_operating_invalidate(rct_window *w)
{
rct_widget *widgets;
Ride *ride;
rct_ride_entry *rideEntry;
rct_string_id format, caption, tooltip;
widgets = window_ride_page_widgets[w->page];
@ -3382,7 +3381,6 @@ static void window_ride_operating_invalidate(rct_window *w)
window_ride_set_pressed_tab(w);
ride = get_ride(w->number);
rideEntry = get_ride_entry_by_ride(ride);
set_format_arg(0, rct_string_id, ride->name);
set_format_arg(2, uint32, ride->name_arguments);
@ -3397,8 +3395,8 @@ static void window_ride_operating_invalidate(rct_window *w)
);
// Lift hill speed
if ((rideEntry->enabledTrackPieces & (1ULL << TRACK_LIFT_HILL)) &&
track_piece_is_available_for_ride_type(ride->type, TRACK_LIFT_HILL)) {
if (track_piece_is_available_for_ride_type(ride->type, TRACK_LIFT_HILL))
{
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_LABEL].type = WWT_LABEL;
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED].type = WWT_SPINNER;
window_ride_operating_widgets[WIDX_LIFT_HILL_SPEED_INCREASE].type = WWT_BUTTON;

View File

@ -301,12 +301,15 @@ namespace OpenRCT2
// return false;
// } //This comment was relocated so it would stay where it was in relation to the following lines of code.
auto rct2InstallPath = GetOrPromptRCT2Path();
if (rct2InstallPath.empty())
if (!gOpenRCT2Headless)
{
return false;
auto rct2InstallPath = GetOrPromptRCT2Path();
if (rct2InstallPath.empty())
{
return false;
}
_env->SetBasePath(DIRBASE::RCT2, rct2InstallPath);
}
_env->SetBasePath(DIRBASE::RCT2, rct2InstallPath);
_objectRepository = CreateObjectRepository(_env);
_objectManager = CreateObjectManager(_objectRepository);

View File

@ -80,9 +80,9 @@ namespace String
sint32 Compare(const utf8 * a, const utf8 * b, bool ignoreCase)
{
if (a == b) return true;
if (a == nullptr || b == nullptr) return false;
if (a == b) return 0;
if (a == nullptr) a = "";
if (b == nullptr) b = "";
if (ignoreCase)
{
return _stricmp(a, b);

View File

@ -56,6 +56,7 @@ enum {
G1_FLAG_BMP = (1 << 0), // Image data is encoded as raw pixels (no transparency)
G1_FLAG_1 = (1 << 1),
G1_FLAG_RLE_COMPRESSION = (1 << 2), // Image data is encoded using RCT2's form of run length encoding
G1_FLAG_PALETTE = (1 << 3), // Image data is a sequence of palette entries R8G8B8
G1_FLAG_HAS_ZOOM_SPRITE = (1 << 4), // Use a different sprite for higher zoom levels
G1_FLAG_NO_ZOOM_DRAW = (1 << 5), // Does not get drawn at higher zoom levels (only zoom 0)
};
@ -339,6 +340,7 @@ void scrolling_text_initialise_bitmaps();
sint32 scrolling_text_setup(struct paint_session * session, rct_string_id stringId, uint16 scroll, uint16 scrollingMode);
rct_size16 FASTCALL gfx_get_sprite_size(uint32 image_id);
size_t g1_calculate_data_size(const rct_g1_element * g1);
void mask_scalar(sint32 width, sint32 height, const uint8 * RESTRICT maskSrc, const uint8 * RESTRICT colourSrc,
uint8 * RESTRICT dst, sint32 maskWrap, sint32 colourWrap, sint32 dstWrap);

View File

@ -379,7 +379,10 @@ bool gfx_load_csg()
{
_csg.elements[i].offset += (uintptr_t)_csg.data;
// RCT1 used zoomed offsets that counted from the beginning of the file, rather than from the current sprite.
_csg.elements[i].zoomed_offset = i - (SPR_CSG_BEGIN + _csg.elements[i].zoomed_offset);
if (_csg.elements[i].zoomed_offset != 0)
{
_csg.elements[i].zoomed_offset = i - (SPR_CSG_BEGIN + _csg.elements[i].zoomed_offset);
}
}
_csgLoaded = true;
return true;
@ -858,3 +861,36 @@ rct_size16 FASTCALL gfx_get_sprite_size(uint32 image_id)
return size;
}
size_t g1_calculate_data_size(const rct_g1_element * g1)
{
if (g1->flags & G1_FLAG_PALETTE)
{
return g1->width * 3;
}
else if (g1->flags & G1_FLAG_RLE_COMPRESSION)
{
if (g1->offset == nullptr)
{
return 0;
}
else
{
uint16 * offsets = (uint16 *)g1->offset;
uint8 * ptr = g1->offset + offsets[g1->height - 1];
bool endOfLine = false;
do
{
uint8 chunk0 = *ptr++;
ptr++; // offset
uint8 chunkSize = chunk0 & 0x7F;
ptr += chunkSize;
endOfLine = (chunk0 & 0x80) != 0;
} while (!endOfLine);
return ptr - g1->offset;
}
}
else
{
return g1->width * g1->height;
}
}

View File

@ -83,6 +83,20 @@ void utf8_remove_format_codes(utf8 * text, bool allowcolours)
*dstCh = 0;
}
uint8 language_get_id_from_locale(const char * locale)
{
uint8 i = 0;
for (const auto &langDesc : LanguagesDescriptors)
{
if (String::Equals(locale, langDesc.locale))
{
return i;
}
i++;
}
return LANGUAGE_UNDEFINED;
}
const char * language_get_string(rct_string_id id)
{
const char * result = nullptr;

View File

@ -94,6 +94,7 @@ extern const utf8 BlackLeftArrowString[];
extern const utf8 BlackRightArrowString[];
extern const utf8 CheckBoxMarkString[];
uint8 language_get_id_from_locale(const char * locale);
const char *language_get_string(rct_string_id id);
bool language_open(sint32 id);
void language_close_all();

View File

@ -15,11 +15,11 @@
#pragma endregion
#include "../core/IStream.hpp"
#include "BannerObject.h"
#include "../drawing/Drawing.h"
#include "../localisation/Language.h"
#include "../object/Object.h"
#include "BannerObject.h"
#include "ObjectJsonHelpers.h"
#include "ObjectList.h"
void BannerObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
@ -31,12 +31,12 @@ void BannerObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
_legacyType.banner.scenery_tab_id = stream->ReadValue<uint8>();
stream->Seek(1, STREAM_SEEK_CURRENT);
GetStringTable()->Read(context, stream, OBJ_STRING_ID_NAME);
GetStringTable().Read(context, stream, OBJ_STRING_ID_NAME);
rct_object_entry sgEntry = stream->ReadValue<rct_object_entry>();
SetPrimarySceneryGroup(&sgEntry);
GetImageTable()->Read(context, stream);
GetImageTable().Read(context, stream);
// Validate properties
if (_legacyType.large_scenery.price <= 0)
@ -61,15 +61,15 @@ void BannerObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
void BannerObject::Load()
{
GetStringTable()->Sort();
GetStringTable().Sort();
_legacyType.name = language_allocate_object_string(GetName());
_legacyType.image = gfx_object_allocate_images(GetImageTable()->GetImages(), GetImageTable()->GetCount());
_legacyType.image = gfx_object_allocate_images(GetImageTable().GetImages(), GetImageTable().GetCount());
}
void BannerObject::Unload()
{
language_free_object_string(_legacyType.name);
gfx_object_free_images(_legacyType.image, GetImageTable()->GetCount());
gfx_object_free_images(_legacyType.image, GetImageTable().GetCount());
_legacyType.name = 0;
_legacyType.image = 0;
@ -84,3 +84,18 @@ void BannerObject::DrawPreview(rct_drawpixelinfo * dpi, sint32 width, sint32 hei
gfx_draw_sprite(dpi, imageId + 0, x - 12, y + 8, 0);
gfx_draw_sprite(dpi, imageId + 1, x - 12, y + 8, 0);
}
void BannerObject::ReadJson(IReadObjectContext * context, const json_t * root)
{
auto properties = json_object_get(root, "properties");
_legacyType.banner.scrolling_mode = json_integer_value(json_object_get(properties, "scrollingMode"));
_legacyType.banner.price = json_integer_value(json_object_get(properties, "price"));
_legacyType.banner.flags = ObjectJsonHelpers::GetFlags<uint8>(properties, {
{ "hasPrimaryColour", BANNER_ENTRY_FLAG_HAS_PRIMARY_COLOUR }});
SetPrimarySceneryGroup(ObjectJsonHelpers::GetString(json_object_get(properties, "sceneryGroup")));
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
}

View File

@ -31,6 +31,7 @@ public:
void * GetLegacyData() override { return &_legacyType; }
void ReadLegacy(IReadObjectContext * context, IStream * stream) override;
void ReadJson(IReadObjectContext * context, const json_t * root) override;
void Load() override;
void Unload() override;

View File

@ -16,10 +16,10 @@
#include "../core/IStream.hpp"
#include "../core/String.hpp"
#include "EntranceObject.h"
#include "../drawing/Drawing.h"
#include "../localisation/Localisation.h"
#include "EntranceObject.h"
#include "ObjectJsonHelpers.h"
void EntranceObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
{
@ -27,8 +27,8 @@ void EntranceObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
_legacyType.scrolling_mode = stream->ReadValue<uint8>();
_legacyType.text_height = stream->ReadValue<uint8>();
GetStringTable()->Read(context, stream, OBJ_STRING_ID_NAME);
GetImageTable()->Read(context, stream);
GetStringTable().Read(context, stream, OBJ_STRING_ID_NAME);
GetImageTable().Read(context, stream);
// Fix issue #1705: The Medieval entrance from Time Twister has a straight banner,
// but scrolls its text as if it a curved one.
@ -41,15 +41,15 @@ void EntranceObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
void EntranceObject::Load()
{
GetStringTable()->Sort();
GetStringTable().Sort();
_legacyType.string_idx = language_allocate_object_string(GetName());
_legacyType.image_id = gfx_object_allocate_images(GetImageTable()->GetImages(), GetImageTable()->GetCount());
_legacyType.image_id = gfx_object_allocate_images(GetImageTable().GetImages(), GetImageTable().GetCount());
}
void EntranceObject::Unload()
{
language_free_object_string(_legacyType.string_idx);
gfx_object_free_images(_legacyType.image_id, GetImageTable()->GetCount());
gfx_object_free_images(_legacyType.image_id, GetImageTable().GetCount());
_legacyType.string_idx = 0;
_legacyType.image_id = 0;
@ -65,3 +65,13 @@ void EntranceObject::DrawPreview(rct_drawpixelinfo * dpi, sint32 width, sint32 h
gfx_draw_sprite(dpi, imageId + 0, x + 0, y + 28, 0);
gfx_draw_sprite(dpi, imageId + 2, x + 32, y + 44, 0);
}
void EntranceObject::ReadJson(IReadObjectContext * context, const json_t * root)
{
auto properties = json_object_get(root, "properties");
_legacyType.scrolling_mode = json_integer_value(json_object_get(properties, "scrollingMode"));
_legacyType.text_height = json_integer_value(json_object_get(properties, "textHeight"));
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
}

View File

@ -31,6 +31,7 @@ public:
void * GetLegacyData() override { return &_legacyType; }
void ReadLegacy(IReadObjectContext * context, IStream * stream) override;
void ReadJson(IReadObjectContext * context, const json_t * root) override;
void Load() override;
void Unload() override;

View File

@ -14,13 +14,15 @@
*****************************************************************************/
#pragma endregion
#include <unordered_map>
#include "../core/IStream.hpp"
#include "FootpathItemObject.h"
#include "../drawing/Drawing.h"
#include "../interface/Cursors.h"
#include "../localisation/Localisation.h"
#include "../object/Object.h"
#include "ObjectList.h"
#include "FootpathItemObject.h"
#include "ObjectJsonHelpers.h"
void FootpathItemObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
{
@ -32,12 +34,12 @@ void FootpathItemObject::ReadLegacy(IReadObjectContext * context, IStream * stre
_legacyType.path_bit.scenery_tab_id = stream->ReadValue<uint8>();
stream->Seek(1, STREAM_SEEK_CURRENT);
GetStringTable()->Read(context, stream, OBJ_STRING_ID_NAME);
GetStringTable().Read(context, stream, OBJ_STRING_ID_NAME);
rct_object_entry sgEntry = stream->ReadValue<rct_object_entry>();
SetPrimarySceneryGroup(&sgEntry);
GetImageTable()->Read(context, stream);
GetImageTable().Read(context, stream);
// Validate properties
if (_legacyType.large_scenery.price <= 0)
@ -62,9 +64,9 @@ void FootpathItemObject::ReadLegacy(IReadObjectContext * context, IStream * stre
void FootpathItemObject::Load()
{
GetStringTable()->Sort();
GetStringTable().Sort();
_legacyType.name = language_allocate_object_string(GetName());
_legacyType.image = gfx_object_allocate_images(GetImageTable()->GetImages(), GetImageTable()->GetCount());
_legacyType.image = gfx_object_allocate_images(GetImageTable().GetImages(), GetImageTable().GetCount());
_legacyType.path_bit.scenery_tab_id = 0xFF;
}
@ -72,7 +74,7 @@ void FootpathItemObject::Load()
void FootpathItemObject::Unload()
{
language_free_object_string(_legacyType.name);
gfx_object_free_images(_legacyType.image, GetImageTable()->GetCount());
gfx_object_free_images(_legacyType.image, GetImageTable().GetCount());
_legacyType.name = 0;
_legacyType.image = 0;
@ -84,3 +86,37 @@ void FootpathItemObject::DrawPreview(rct_drawpixelinfo * dpi, sint32 width, sint
sint32 y = height / 2;
gfx_draw_sprite(dpi, _legacyType.image, x - 22, y - 24, 0);
}
static uint8 ParseDrawType(const std::string &s)
{
if (s == "lamp") return PATH_BIT_DRAW_TYPE_LIGHTS;
if (s == "bin") return PATH_BIT_DRAW_TYPE_BINS;
if (s == "bench") return PATH_BIT_DRAW_TYPE_BENCHES;
if (s == "fountain") return PATH_BIT_DRAW_TYPE_JUMPING_FOUNTAINS;
return PATH_BIT_DRAW_TYPE_LIGHTS;
}
void FootpathItemObject::ReadJson(IReadObjectContext * context, const json_t * root)
{
auto properties = json_object_get(root, "properties");
_legacyType.path_bit.draw_type = ParseDrawType(ObjectJsonHelpers::GetString(properties, "renderAs"));
_legacyType.path_bit.tool_id = ObjectJsonHelpers::ParseCursor(ObjectJsonHelpers::GetString(properties, "cursor"), CURSOR_LAMPPOST_DOWN);
_legacyType.path_bit.price = json_integer_value(json_object_get(properties, "price"));
SetPrimarySceneryGroup(ObjectJsonHelpers::GetString(json_object_get(properties, "sceneryGroup")));
// Flags
_legacyType.path_bit.flags = ObjectJsonHelpers::GetFlags<uint16>(properties, {
{ "isBin", PATH_BIT_FLAG_IS_BIN },
{ "isBench", PATH_BIT_FLAG_IS_BENCH },
{ "isBreakable", PATH_BIT_FLAG_BREAKABLE },
{ "isLamp", PATH_BIT_FLAG_LAMP },
{ "isJumpingFountainWater", PATH_BIT_FLAG_JUMPING_FOUNTAIN_WATER },
{ "isJumpingFountainSnow", PATH_BIT_FLAG_JUMPING_FOUNTAIN_SNOW },
{ "isAllowedOnQueue", PATH_BIT_FLAG_DONT_ALLOW_ON_QUEUE },
{ "isAllowedOnSlope", PATH_BIT_FLAG_DONT_ALLOW_ON_SLOPE },
{ "isTelevision", PATH_BIT_FLAG_IS_QUEUE_SCREEN }});
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
}

View File

@ -31,6 +31,7 @@ public:
void * GetLegacyData() override { return &_legacyType; }
void ReadLegacy(IReadObjectContext * context, IStream * stream) override;
void ReadJson(IReadObjectContext * context, const json_t * root) override;
void Load() override;
void Unload() override;

View File

@ -15,11 +15,11 @@
#pragma endregion
#include "../core/IStream.hpp"
#include "FootpathObject.h"
#include "../drawing/Drawing.h"
#include "../localisation/Language.h"
#include "../world/Footpath.h"
#include "FootpathObject.h"
#include "ObjectJsonHelpers.h"
void FootpathObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
{
@ -29,8 +29,8 @@ void FootpathObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
_legacyType.scrolling_mode = stream->ReadValue<uint8>();
stream->Seek(1, STREAM_SEEK_CURRENT);
GetStringTable()->Read(context, stream, OBJ_STRING_ID_NAME);
GetImageTable()->Read(context, stream);
GetStringTable().Read(context, stream, OBJ_STRING_ID_NAME);
GetImageTable().Read(context, stream);
// Validate properties
if (_legacyType.support_type >= FOOTPATH_ENTRY_SUPPORT_TYPE_COUNT)
@ -41,16 +41,16 @@ void FootpathObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
void FootpathObject::Load()
{
GetStringTable()->Sort();
GetStringTable().Sort();
_legacyType.string_idx = language_allocate_object_string(GetName());
_legacyType.image = gfx_object_allocate_images(GetImageTable()->GetImages(), GetImageTable()->GetCount());
_legacyType.image = gfx_object_allocate_images(GetImageTable().GetImages(), GetImageTable().GetCount());
_legacyType.bridge_image = _legacyType.image + 109;
}
void FootpathObject::Unload()
{
language_free_object_string(_legacyType.string_idx);
gfx_object_free_images(_legacyType.image, GetImageTable()->GetCount());
gfx_object_free_images(_legacyType.image, GetImageTable().GetCount());
_legacyType.string_idx = 0;
_legacyType.image = 0;
@ -63,3 +63,25 @@ void FootpathObject::DrawPreview(rct_drawpixelinfo * dpi, sint32 width, sint32 h
gfx_draw_sprite(dpi, _legacyType.image + 71, x - 49, y - 17, 0);
gfx_draw_sprite(dpi, _legacyType.image + 72, x + 4, y - 17, 0);
}
static uint8 ParseSupportType(const std::string &s)
{
if (s == "pole") return FOOTPATH_ENTRY_SUPPORT_TYPE_POLE;
else /* if (s == "box") */ return FOOTPATH_ENTRY_SUPPORT_TYPE_BOX;
}
void FootpathObject::ReadJson(IReadObjectContext * context, const json_t * root)
{
auto properties = json_object_get(root, "properties");
_legacyType.support_type = ParseSupportType(ObjectJsonHelpers::GetString(json_object_get(properties, "supportType")));
_legacyType.scrolling_mode = json_integer_value(json_object_get(properties, "scrollingMode"));
// Flags
_legacyType.flags = ObjectJsonHelpers::GetFlags<uint8>(properties, {
{ "hasSupportImages", FOOTPATH_ENTRY_FLAG_HAS_SUPPORT_BASE_SPRITE },
{ "hasElevatedPathImages", FOOTPATH_ENTRY_FLAG_HAS_PATH_BASE_SPRITE },
{ "editorOnly", FOOTPATH_ENTRY_FLAG_SHOW_ONLY_IN_SCENARIO_EDITOR } });
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
}

View File

@ -31,6 +31,7 @@ public:
void * GetLegacyData() override { return &_legacyType; }
void ReadLegacy(IReadObjectContext * context, IStream * stream) override;
void ReadJson(IReadObjectContext * context, const json_t * root) override;
void Load() override;
void Unload() override;

View File

@ -15,18 +15,22 @@
#pragma endregion
#include <algorithm>
#include <memory>
#include <stdexcept>
#include "../core/IStream.hpp"
#include "../core/Memory.hpp"
#include "../OpenRCT2.h"
#include "ImageTable.h"
#include "Object.h"
ImageTable::~ImageTable()
{
Memory::Free(_data);
_data = nullptr;
_dataSize = 0;
if (_data == nullptr)
{
for (auto &entry : _entries)
{
delete [] entry.offset;
}
}
}
void ImageTable::Read(IReadObjectContext * context, IStream * stream)
@ -49,16 +53,17 @@ void ImageTable::Read(IReadObjectContext * context, IStream * stream)
imageDataSize = (uint32)remainingBytes;
}
_dataSize = imageDataSize;
_data = Memory::Reallocate(_data, _dataSize);
if (_data == nullptr)
auto dataSize = (size_t)imageDataSize;
auto data = std::make_unique<uint8[]>(dataSize);
if (data == nullptr)
{
context->LogError(OBJECT_ERROR_BAD_IMAGE_TABLE, "Image table too large.");
throw std::runtime_error("Image table too large.");
}
// Read g1 element headers
uintptr_t imageDataBase = (uintptr_t)_data;
uintptr_t imageDataBase = (uintptr_t)data.get();
std::vector<rct_g1_element> newEntries;
for (uint32 i = 0; i < numImages; i++)
{
rct_g1_element g1Element;
@ -73,23 +78,22 @@ void ImageTable::Read(IReadObjectContext * context, IStream * stream)
g1Element.flags = stream->ReadValue<uint16>();
g1Element.zoomed_offset = stream->ReadValue<uint16>();
_entries.push_back(g1Element);
newEntries.push_back(g1Element);
}
// Read g1 element data
size_t readBytes = (size_t)stream->TryRead(_data, _dataSize);
size_t readBytes = (size_t)stream->TryRead(data.get(), dataSize);
// If data is shorter than expected (some custom objects are unfortunately like that)
size_t unreadBytes = _dataSize - readBytes;
size_t unreadBytes = dataSize - readBytes;
if (unreadBytes > 0)
{
uint8 * ptr = (uint8*)(((uintptr_t)_data) + readBytes);
std::fill_n(ptr, unreadBytes, 0);
std::fill_n(data.get() + readBytes, unreadBytes, 0);
context->LogWarning(OBJECT_ERROR_BAD_IMAGE_TABLE, "Image table size shorter than expected.");
}
// TODO validate the image data to prevent crashes in-game
_data = std::move(data);
_entries.insert(_entries.end(), newEntries.begin(), newEntries.end());
}
catch (const std::exception &)
{
@ -97,3 +101,19 @@ void ImageTable::Read(IReadObjectContext * context, IStream * stream)
throw;
}
}
void ImageTable::AddImage(const rct_g1_element * g1)
{
rct_g1_element newg1 = *g1;
auto length = g1_calculate_data_size(g1);
if (length == 0)
{
newg1.offset = nullptr;
}
else
{
newg1.offset = new uint8[length];
std::copy_n(g1->offset, length, newg1.offset);
}
_entries.push_back(newg1);
}

View File

@ -16,9 +16,9 @@
#pragma once
#include <memory>
#include <vector>
#include "../common.h"
#include "../drawing/Drawing.h"
interface IReadObjectContext;
@ -27,14 +27,17 @@ interface IStream;
class ImageTable
{
private:
std::unique_ptr<uint8[]> _data;
std::vector<rct_g1_element> _entries;
void * _data = nullptr;
size_t _dataSize = 0;
public:
ImageTable() = default;
ImageTable(const ImageTable &) = delete;
ImageTable & operator=(const ImageTable &) = delete;
~ImageTable();
void Read(IReadObjectContext * context, IStream * stream);
const rct_g1_element * GetImages() const { return _entries.data(); }
uint32 GetCount() const { return (uint32)_entries.size(); }
void AddImage(const rct_g1_element * g1);
};

View File

@ -14,12 +14,17 @@
*****************************************************************************/
#pragma endregion
#pragma warning(disable : 4706) // assignment within conditional expression
#include <algorithm>
#include "../core/IStream.hpp"
#include "../core/Memory.hpp"
#include "LargeSceneryObject.h"
#include "../core/Util.hpp"
#include "../drawing/Drawing.h"
#include "../interface/Cursors.h"
#include "../localisation/Language.h"
#include "LargeSceneryObject.h"
#include "ObjectJsonHelpers.h"
void LargeSceneryObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
{
@ -33,7 +38,7 @@ void LargeSceneryObject::ReadLegacy(IReadObjectContext * context, IStream * stre
_legacyType.large_scenery.scrolling_mode = stream->ReadValue<uint8>();
stream->Seek(4, STREAM_SEEK_CURRENT);
GetStringTable()->Read(context, stream, OBJ_STRING_ID_NAME);
GetStringTable().Read(context, stream, OBJ_STRING_ID_NAME);
rct_object_entry sgEntry = stream->ReadValue<rct_object_entry>();
SetPrimarySceneryGroup(&sgEntry);
@ -47,7 +52,7 @@ void LargeSceneryObject::ReadLegacy(IReadObjectContext * context, IStream * stre
_tiles = ReadTiles(stream);
GetImageTable()->Read(context, stream);
GetImageTable().Read(context, stream);
// Validate properties
if (_legacyType.large_scenery.price <= 0)
@ -67,9 +72,9 @@ void LargeSceneryObject::ReadLegacy(IReadObjectContext * context, IStream * stre
void LargeSceneryObject::Load()
{
GetStringTable()->Sort();
GetStringTable().Sort();
_legacyType.name = language_allocate_object_string(GetName());
_baseImageId = gfx_object_allocate_images(GetImageTable()->GetImages(), GetImageTable()->GetCount());
_baseImageId = gfx_object_allocate_images(GetImageTable().GetImages(), GetImageTable().GetCount());
_legacyType.image = _baseImageId;
_legacyType.large_scenery.tiles = _tiles.data();
@ -79,19 +84,20 @@ void LargeSceneryObject::Load()
_legacyType.large_scenery.text_image = _legacyType.image;
if (_3dFont->flags & LARGE_SCENERY_TEXT_FLAG_VERTICAL)
{
_legacyType.image += _3dFont->var_D * 2;
_legacyType.image += _3dFont->num_images * 2;
}
else
{
_legacyType.image += _3dFont->var_D * 4;
_legacyType.image += _3dFont->num_images * 4;
}
_legacyType.large_scenery.text = _3dFont.get();
}
}
void LargeSceneryObject::Unload()
{
language_free_object_string(_legacyType.name);
gfx_object_free_images(_baseImageId, GetImageTable()->GetCount());
gfx_object_free_images(_baseImageId, GetImageTable().GetCount());
_legacyType.name = 0;
_legacyType.image = 0;
@ -118,3 +124,147 @@ std::vector<rct_large_scenery_tile> LargeSceneryObject::ReadTiles(IStream * stre
tiles.push_back({ -1, -1, -1, 255, 0xFFFF });
return tiles;
}
void LargeSceneryObject::ReadJson(IReadObjectContext * context, const json_t * root)
{
auto properties = json_object_get(root, "properties");
_legacyType.large_scenery.tool_id = ObjectJsonHelpers::ParseCursor(ObjectJsonHelpers::GetString(properties, "cursor"), CURSOR_STATUE_DOWN);
_legacyType.large_scenery.price = json_integer_value(json_object_get(properties, "price"));
_legacyType.large_scenery.removal_price = json_integer_value(json_object_get(properties, "removalPrice"));
auto jScrollingMode = json_object_get(properties, "scrollingMode");
_legacyType.large_scenery.scrolling_mode = jScrollingMode != nullptr ?
json_integer_value(jScrollingMode) :
-1;
// Flags
_legacyType.large_scenery.flags = ObjectJsonHelpers::GetFlags<uint8>(properties, {
{ "hasPrimaryColour", LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR },
{ "hasSecondaryColour", LARGE_SCENERY_FLAG_HAS_SECONDARY_COLOUR },
{ "isAnimated", LARGE_SCENERY_FLAG_ANIMATED },
{ "isPhotogenic", LARGE_SCENERY_FLAG_PHOTOGENIC } });
// Tiles
auto jTiles = json_object_get(properties, "tiles");
if (jTiles != nullptr)
{
_tiles = ReadJsonTiles(jTiles);
}
// Read text
auto j3dFont = json_object_get(properties, "3dFont");
if (j3dFont != nullptr)
{
_3dFont = ReadJson3dFont(j3dFont);
_legacyType.large_scenery.flags |= LARGE_SCENERY_FLAG_3D_TEXT;
}
SetPrimarySceneryGroup(ObjectJsonHelpers::GetString(json_object_get(properties, "sceneryGroup")));
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
}
std::vector<rct_large_scenery_tile> LargeSceneryObject::ReadJsonTiles(const json_t * jTiles)
{
std::vector<rct_large_scenery_tile> tiles;
size_t index;
const json_t * jTile;
json_array_foreach(jTiles, index, jTile)
{
rct_large_scenery_tile tile = { 0 };
tile.x_offset = json_integer_value(json_object_get(jTile, "x"));
tile.y_offset = json_integer_value(json_object_get(jTile, "y"));
tile.z_offset = json_integer_value(json_object_get(jTile, "z"));
tile.z_clearance = json_integer_value(json_object_get(jTile, "clearance"));
if (!ObjectJsonHelpers::GetBoolean(jTile, "hasSupports"))
{
tile.flags |= LARGE_SCENERY_TILE_FLAG_NO_SUPPORTS;
}
if (ObjectJsonHelpers::GetBoolean(jTile, "allowSupportsAbove"))
{
tile.flags |= LARGE_SCENERY_TILE_FLAG_ALLOW_SUPPORTS_ABOVE;
}
// All corners are by default occupied
auto jCorners = json_object_get(jTile, "corners");
auto corners = 0xF;
if (jCorners != nullptr)
{
corners = json_integer_value(jCorners);
}
tile.flags |= (corners & 0xFF) << 12;
auto walls = json_integer_value(json_object_get(jTile, "walls"));
tile.flags |= (walls & 0xFF) << 8;
tiles.push_back(tile);
}
// HACK Add end of tiles marker
// We should remove this later by improving the code base to use tiles array length
tiles.push_back({ -1, -1, -1, 0xFF, 0xFFFF });
return tiles;
}
std::unique_ptr<rct_large_scenery_text> LargeSceneryObject::ReadJson3dFont(const json_t * j3dFont)
{
auto font = std::make_unique<rct_large_scenery_text>();
auto jOffsets = json_object_get(j3dFont, "offsets");
if (jOffsets != nullptr)
{
auto offsets = ReadJsonOffsets(jOffsets);
auto numOffsets = std::min(Util::CountOf(font->offset), offsets.size());
std::copy_n(offsets.data(), numOffsets, font->offset);
}
font->max_width = json_integer_value(json_object_get(j3dFont, "maxWidth"));
font->num_images = json_integer_value(json_object_get(j3dFont, "numImages"));
font->flags = ObjectJsonHelpers::GetFlags<uint8>(j3dFont, {
{ "isVertical", LARGE_SCENERY_TEXT_FLAG_VERTICAL },
{ "isTwoLine", LARGE_SCENERY_TEXT_FLAG_TWO_LINE } });
auto jGlyphs = json_object_get(j3dFont, "glyphs");
if (jGlyphs != nullptr)
{
auto glyphs = ReadJsonGlyphs(jGlyphs);
auto numGlyphs = std::min(Util::CountOf(font->glyphs), glyphs.size());
std::copy_n(glyphs.data(), numGlyphs, font->glyphs);
}
return font;
}
std::vector<LocationXY16> LargeSceneryObject::ReadJsonOffsets(const json_t * jOffsets)
{
std::vector<LocationXY16> offsets;
size_t index;
const json_t * jOffset;
json_array_foreach(jOffsets, index, jOffset)
{
LocationXY16 offset = { 0 };
offset.x = json_integer_value(json_object_get(jOffset, "x"));
offset.y = json_integer_value(json_object_get(jOffset, "y"));
offsets.push_back(offset);
}
return offsets;
}
std::vector<rct_large_scenery_text_glyph> LargeSceneryObject::ReadJsonGlyphs(const json_t * jGlpyhs)
{
std::vector<rct_large_scenery_text_glyph> glyphs;
size_t index;
const json_t * jGlyph;
json_array_foreach(jGlpyhs, index, jGlyph)
{
rct_large_scenery_text_glyph glyph;
glyph.image_offset = json_integer_value(json_object_get(jGlyph, "image"));
glyph.width = json_integer_value(json_object_get(jGlyph, "width"));
glyph.height = json_integer_value(json_object_get(jGlyph, "height"));
glyphs.push_back(glyph);
}
return glyphs;
}

View File

@ -35,6 +35,7 @@ public:
void * GetLegacyData() override { return &_legacyType; }
void ReadLegacy(IReadObjectContext * context, IStream * stream) override;
void ReadJson(IReadObjectContext * context, const json_t * root) override;
void Load() override;
void Unload() override;
@ -42,4 +43,8 @@ public:
private:
static std::vector<rct_large_scenery_tile> ReadTiles(IStream * stream);
static std::vector<rct_large_scenery_tile> ReadJsonTiles(const json_t * jTiles);
static std::unique_ptr<rct_large_scenery_text> ReadJson3dFont(const json_t * j3dFont);
static std::vector<LocationXY16> ReadJsonOffsets(const json_t * jOffsets);
static std::vector<rct_large_scenery_text_glyph> ReadJsonGlyphs(const json_t * jGlpyhs);
};

View File

@ -71,7 +71,7 @@ std::string Object::GetString(uint8 index) const
auto sz = GetOverrideString(index);
if (sz.empty())
{
sz = GetStringTable()->GetString(index);
sz = GetStringTable().GetString(index);
}
return sz;
}

View File

@ -17,6 +17,7 @@
#pragma once
#include "../common.h"
#include "../core/Json.hpp"
#include "ImageTable.h"
#include "StringTable.h"
@ -110,16 +111,6 @@ struct rct_object_filters {
assert_struct_size(rct_object_filters, 3);
#pragma pack(pop)
enum OBJ_STRING_ID
{
OBJ_STRING_ID_NAME,
OBJ_STRING_ID_DESCRIPTION,
OBJ_STRING_ID_SCENARIO_NAME = 0,
OBJ_STRING_ID_PARK_NAME = 1,
OBJ_STRING_ID_SCENARIO_DETAILS = 2,
OBJ_STRING_ID_CAPACITY = 2,
};
interface IStream;
struct ObjectRepositoryItem;
struct rct_drawpixelinfo;
@ -128,6 +119,8 @@ interface IReadObjectContext
{
virtual ~IReadObjectContext() = default;
virtual bool ShouldLoadImages() abstract;
virtual void LogWarning(uint32 code, const utf8 * text) abstract;
virtual void LogError(uint32 code, const utf8 * text) abstract;
};
@ -146,9 +139,9 @@ private:
ImageTable _imageTable;
protected:
StringTable * GetStringTable() { return &_stringTable; }
const StringTable * GetStringTable() const { return &_stringTable; }
ImageTable * GetImageTable() { return &_imageTable; }
StringTable & GetStringTable() { return _stringTable; }
const StringTable & GetStringTable() const { return _stringTable; }
ImageTable & GetImageTable() { return _imageTable; }
std::string GetOverrideString(uint8 index) const;
std::string GetString(uint8 index) const;
@ -168,6 +161,7 @@ public:
const rct_object_entry * GetObjectEntry() const { return &_objectEntry; }
virtual void * GetLegacyData() abstract;
virtual void ReadJson(IReadObjectContext * context, const json_t * root) { }
virtual void ReadLegacy(IReadObjectContext * context, IStream * stream) abstract;
virtual void Load() abstract;
virtual void Unload() abstract;
@ -179,6 +173,8 @@ public:
virtual void SetRepositoryItem(ObjectRepositoryItem * item) const { }
const ImageTable & GetImageTable() const { return _imageTable; }
rct_object_entry GetScgWallsHeader();
rct_object_entry GetScgPathXHeader();
rct_object_entry CreateHeader(const char name[9], uint32 flags, uint32 checksum);

View File

@ -16,9 +16,11 @@
#include "../core/Console.hpp"
#include "../core/FileStream.hpp"
#include "../core/Json.hpp"
#include "../core/Memory.hpp"
#include "../core/MemoryStream.h"
#include "../core/String.hpp"
#include "../OpenRCT2.h"
#include "../rct12/SawyerChunkReader.h"
#include "BannerObject.h"
#include "EntranceObject.h"
@ -39,23 +41,24 @@
class ReadObjectContext : public IReadObjectContext
{
private:
utf8 * _objectName;
bool _wasWarning = false;
bool _wasError = false;
std::string _objectName;
bool _loadImages;
bool _wasWarning = false;
bool _wasError = false;
public:
bool WasWarning() const { return _wasWarning; }
bool WasError() const { return _wasError; }
explicit ReadObjectContext(const utf8 * objectFileName)
ReadObjectContext(const std::string &objectName, bool loadImages)
: _objectName(objectName),
_loadImages(loadImages)
{
_objectName = String::Duplicate(objectFileName);
}
~ReadObjectContext() override
bool ShouldLoadImages() override
{
Memory::Free(_objectName);
_objectName = nullptr;
return _loadImages;
}
void LogWarning(uint32 code, const utf8 * text) override
@ -64,7 +67,7 @@ public:
if (!String::IsNullOrEmpty(text))
{
log_verbose("[%s] Warning: %s", _objectName, text);
Console::Error::WriteLine("[%s] Warning: %s", _objectName.c_str(), text);
}
}
@ -74,7 +77,7 @@ public:
if (!String::IsNullOrEmpty(text))
{
Console::Error::WriteLine("[%s] Error: %s", _objectName, text);
Console::Error::WriteLine("[%s] Error: %s", _objectName.c_str(), text);
}
}
};
@ -119,7 +122,7 @@ namespace ObjectFactory
log_verbose(" size: %zu", chunk->GetLength());
auto chunkStream = MemoryStream(chunk->GetData(), chunk->GetLength());
auto readContext = ReadObjectContext(objectName);
auto readContext = ReadObjectContext(objectName, !gOpenRCT2Headless);
ReadObjectLegacy(result, &readContext, &chunkStream);
if (readContext.WasError())
{
@ -128,8 +131,6 @@ namespace ObjectFactory
}
catch (const std::exception &)
{
Console::Error::WriteLine("Unable to open or read '%s'", path);
delete result;
result = nullptr;
}
@ -147,7 +148,7 @@ namespace ObjectFactory
utf8 objectName[DAT_NAME_LENGTH + 1];
object_entry_get_name_fixed(objectName, sizeof(objectName), entry);
auto readContext = ReadObjectContext(objectName);
auto readContext = ReadObjectContext(objectName, !gOpenRCT2Headless);
auto chunkStream = MemoryStream(data, dataSize);
ReadObjectLegacy(result, &readContext, &chunkStream);
@ -203,4 +204,68 @@ namespace ObjectFactory
}
return result;
}
static uint8 ParseObjectType(const std::string &s)
{
if (s == "ride") return OBJECT_TYPE_RIDE;
if (s == "footpath") return OBJECT_TYPE_PATHS;
if (s == "footpath_banner") return OBJECT_TYPE_BANNERS;
if (s == "footpath_item") return OBJECT_TYPE_PATH_BITS;
if (s == "scenery_small") return OBJECT_TYPE_SMALL_SCENERY;
if (s == "scenery_large") return OBJECT_TYPE_LARGE_SCENERY;
if (s == "scenery_wall") return OBJECT_TYPE_WALLS;
if (s == "scenery_group") return OBJECT_TYPE_SCENERY_GROUP;
if (s == "park_entrance") return OBJECT_TYPE_PARK_ENTRANCE;
if (s == "water") return OBJECT_TYPE_WATER;
return 0xFF;
}
Object * CreateObjectFromJsonFile(const std::string &path)
{
log_verbose("CreateObjectFromJsonFile(\"%s\")", path.c_str());
Object * result = nullptr;
try
{
auto jRoot = Json::ReadFromFile(path.c_str());
auto jObjectType = json_object_get(jRoot, "objectType");
if (json_is_string(jObjectType))
{
auto objectType = ParseObjectType(json_string_value(jObjectType));
if (objectType != 0xFF)
{
auto id = json_string_value(json_object_get(jRoot, "id"));
rct_object_entry entry = { 0 };
auto originalId = String::ToStd(json_string_value(json_object_get(jRoot, "originalId")));
auto originalName = originalId;
if (originalId.length() == 8 + 1 + 8 + 1 + 8)
{
entry.flags = std::stoul(originalId.substr(0, 8), 0, 16);
originalName = originalId.substr(9, 8);
entry.checksum = std::stoul(originalId.substr(18, 8), 0, 16);
}
auto minLength = std::min<size_t>(8, originalName.length());
memcpy(entry.name, originalName.c_str(), minLength);
result = CreateObject(entry);
auto readContext = ReadObjectContext(id, !gOpenRCT2Headless);
result->ReadJson(&readContext, jRoot);
if (readContext.WasError())
{
throw std::runtime_error("Object has errors");
}
}
}
json_decref(jRoot);
}
catch (const std::runtime_error &err)
{
Console::Error::WriteLine("Unable to open or read '%s': %s", path.c_str(), err.what());
delete result;
result = nullptr;
}
return result;
}
}

View File

@ -26,4 +26,6 @@ namespace ObjectFactory
Object * CreateObjectFromLegacyFile(const utf8 * path);
Object * CreateObjectFromLegacyData(const rct_object_entry * entry, const void * data, size_t dataSize);
Object * CreateObject(const rct_object_entry &entry);
Object * CreateObjectFromJsonFile(const std::string &path);
}

View File

@ -0,0 +1,377 @@
#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#pragma warning(disable : 4706) // assignment within conditional expression
#include <cstdlib>
#include <cstring>
#include <unordered_map>
#include "../Context.h"
#include "../core/File.h"
#include "../core/FileScanner.h"
#include "../core/Math.hpp"
#include "../core/Memory.hpp"
#include "../core/Path.hpp"
#include "../core/String.hpp"
#include "../interface/Cursors.h"
#include "../localisation/Language.h"
#include "../PlatformEnvironment.h"
#include "../sprites.h"
#include "Object.h"
#include "ObjectFactory.h"
#include "ObjectJsonHelpers.h"
using namespace OpenRCT2;
namespace ObjectJsonHelpers
{
bool GetBoolean(const json_t * obj, const std::string &name, bool defaultValue)
{
auto value = json_object_get(obj, name.c_str());
return json_is_boolean(value) ?
json_boolean_value(value) :
defaultValue;
}
std::string GetString(const json_t * value)
{
return json_is_string(value) ?
std::string(json_string_value(value)) :
std::string();
}
std::string GetString(const json_t * obj, const std::string &name, const std::string &defaultValue)
{
auto value = json_object_get(obj, name.c_str());
return json_is_string(value) ?
json_string_value(value) :
defaultValue;
}
sint32 GetInteger(const json_t * obj, const std::string &name, const sint32 &defaultValue)
{
auto value = json_object_get(obj, name.c_str());
if (json_is_integer(value))
{
sint64 val = json_integer_value(value);
if (val >= std::numeric_limits<sint32>::min() &&
val <= std::numeric_limits<sint32>::max())
{
return static_cast<sint32>(val);
}
}
return defaultValue;
}
float GetFloat(const json_t * obj, const std::string &name, const float &defaultValue)
{
auto value = json_object_get(obj, name.c_str());
return json_is_number(value) ?
json_number_value(value) :
defaultValue;
}
std::vector<std::string> GetJsonStringArray(const json_t * arr)
{
std::vector<std::string> result;
if (json_is_array(arr))
{
auto count = json_array_size(arr);
result.reserve(count);
for (size_t i = 0; i < count; i++)
{
auto element = json_string_value(json_array_get(arr, i));
result.push_back(element);
}
}
else if (json_is_string(arr))
{
result.push_back(json_string_value(arr));
}
return result;
}
std::vector<sint32> GetJsonIntegerArray(const json_t * arr)
{
std::vector<sint32> result;
if (json_is_array(arr))
{
auto count = json_array_size(arr);
result.reserve(count);
for (size_t i = 0; i < count; i++)
{
auto element = json_integer_value(json_array_get(arr, i));
result.push_back(element);
}
}
else if (json_is_integer(arr))
{
result.push_back(json_integer_value(arr));
}
return result;
}
uint8 ParseCursor(const std::string &s, uint8 defaultValue)
{
static const std::unordered_map<std::string, uint8> LookupTable
{
{ "CURSOR_BLANK", CURSOR_BLANK },
{ "CURSOR_UP_ARROW", CURSOR_UP_ARROW },
{ "CURSOR_UP_DOWN_ARROW", CURSOR_UP_DOWN_ARROW },
{ "CURSOR_HAND_POINT", CURSOR_HAND_POINT },
{ "CURSOR_ZZZ", CURSOR_ZZZ },
{ "CURSOR_DIAGONAL_ARROWS", CURSOR_DIAGONAL_ARROWS },
{ "CURSOR_PICKER", CURSOR_PICKER },
{ "CURSOR_TREE_DOWN", CURSOR_TREE_DOWN },
{ "CURSOR_FOUNTAIN_DOWN", CURSOR_FOUNTAIN_DOWN },
{ "CURSOR_STATUE_DOWN", CURSOR_STATUE_DOWN },
{ "CURSOR_BENCH_DOWN", CURSOR_BENCH_DOWN },
{ "CURSOR_CROSS_HAIR", CURSOR_CROSS_HAIR },
{ "CURSOR_BIN_DOWN", CURSOR_BIN_DOWN },
{ "CURSOR_LAMPPOST_DOWN", CURSOR_LAMPPOST_DOWN },
{ "CURSOR_FENCE_DOWN", CURSOR_FENCE_DOWN },
{ "CURSOR_FLOWER_DOWN", CURSOR_FLOWER_DOWN },
{ "CURSOR_PATH_DOWN", CURSOR_PATH_DOWN },
{ "CURSOR_DIG_DOWN", CURSOR_DIG_DOWN },
{ "CURSOR_WATER_DOWN", CURSOR_WATER_DOWN },
{ "CURSOR_HOUSE_DOWN", CURSOR_HOUSE_DOWN },
{ "CURSOR_VOLCANO_DOWN", CURSOR_VOLCANO_DOWN },
{ "CURSOR_WALK_DOWN", CURSOR_WALK_DOWN },
{ "CURSOR_PAINT_DOWN", CURSOR_PAINT_DOWN },
{ "CURSOR_ENTRANCE_DOWN", CURSOR_ENTRANCE_DOWN },
{ "CURSOR_HAND_OPEN", CURSOR_HAND_OPEN },
{ "CURSOR_HAND_CLOSED", CURSOR_HAND_CLOSED },
{ "CURSOR_ARROW", CURSOR_ARROW },
};
auto result = LookupTable.find(s);
return (result != LookupTable.end()) ?
result->second :
defaultValue;
}
rct_object_entry ParseObjectEntry(const std::string & s)
{
rct_object_entry entry = { 0 };
std::fill_n(entry.name, sizeof(entry.name), ' ');
auto copyLen = std::min<size_t>(8, s.size());
std::copy_n(s.c_str(), copyLen, entry.name);
return entry;
}
static std::vector<sint32> ParseRange(std::string s)
{
// Currently only supports [###] or [###..###]
std::vector<sint32> result = { };
if (s.length() >= 3 && s[0] == '[' && s[s.length() - 1] == ']')
{
s = s.substr(1, s.length() - 2);
auto parts = String::Split(s, "..");
if (parts.size() == 1)
{
result.push_back(std::stoi(parts[0]));
}
else
{
auto left = std::stoi(parts[0]);
auto right = std::stoi(parts[1]);
if (left <= right)
{
for (auto i = left; i <= right; i++)
{
result.push_back(i);
}
}
else
{
for (auto i = right; i >= left; i--)
{
result.push_back(i);
}
}
}
}
return result;
}
static std::string FindLegacyObject(const std::string &name)
{
const auto env = GetContext()->GetPlatformEnvironment();
auto objectsPath = env->GetDirectoryPath(DIRBASE::RCT2, DIRID::OBJECT);
auto objectPath = Path::Combine(objectsPath, name);
#ifndef _WIN32
if (!File::Exists(objectPath))
{
// UNIX based systems need to search for any files with the same name
// due to case sensitivity.
auto filter = Path::Combine(objectsPath, "*.dat");
auto scanner = std::unique_ptr<IFileScanner>(Path::ScanDirectory(filter, false));
while (scanner->Next())
{
auto relativePath = scanner->GetPathRelative();
if (String::Equals(relativePath, name, true))
{
objectPath = scanner->GetPath();
break;
}
}
}
#endif
return objectPath;
}
static std::vector<rct_g1_element> LoadObjectImages(IReadObjectContext * context, const std::string &name, const std::vector<sint32> &range)
{
std::vector<rct_g1_element> result;
auto objectPath = FindLegacyObject(name);
auto obj = ObjectFactory::CreateObjectFromLegacyFile(objectPath.c_str());
if (obj != nullptr)
{
auto &imgTable = static_cast<const Object *>(obj)->GetImageTable();
auto numImages = (sint32)imgTable.GetCount();
auto images = imgTable.GetImages();
size_t placeHoldersAdded = 0;
for (auto i : range)
{
if (i >= 0 && i < numImages)
{
auto &objg1 = images[i];
auto length = g1_calculate_data_size(&objg1);
auto g1 = objg1;
g1.offset = (uint8 *)std::malloc(length);
std::memcpy(g1.offset, objg1.offset, length);
result.push_back(g1);
}
else
{
auto g1 = rct_g1_element{};
result.push_back(g1);
placeHoldersAdded++;
}
}
delete obj;
// Log place holder information
if (placeHoldersAdded > 0)
{
std::string msg = "Adding " + std::to_string(placeHoldersAdded) + " placeholders";
context->LogWarning(OBJECT_ERROR_INVALID_PROPERTY, msg.c_str());
}
}
else
{
std::string msg = "Unable to open '" + objectPath + "'";
context->LogWarning(OBJECT_ERROR_INVALID_PROPERTY, msg.c_str());
result.resize(range.size());
}
return result;
}
static std::vector<rct_g1_element> ParseImages(IReadObjectContext * context, std::string s)
{
std::vector<rct_g1_element> result;
if (s.empty())
{
rct_g1_element emptyg1 = { 0 };
result.push_back(emptyg1);
}
else if (String::StartsWith(s, "$CSG"))
{
if (is_csg_loaded())
{
auto range = ParseRange(s.substr(4));
if (range.size() > 0)
{
for (auto i : range)
{
auto &csg1 = *gfx_get_g1_element(SPR_CSG_BEGIN + i);
auto length = g1_calculate_data_size(&csg1);
auto g1 = csg1;
g1.offset = (uint8 *)std::malloc(length);
std::memcpy(g1.offset, csg1.offset, length);
result.push_back(g1);
}
}
}
}
else if (String::StartsWith(s, "$RCT2:OBJDATA/"))
{
auto name = s.substr(14);
auto rangeStart = name.find('[');
if (rangeStart != std::string::npos)
{
auto rangeString = name.substr(rangeStart);
auto range = ParseRange(name.substr(rangeStart));
name = name.substr(0, rangeStart);
result = LoadObjectImages(context, name, range);
}
}
return result;
}
static uint8 ParseStringId(const std::string &s)
{
if (s == "name") return OBJ_STRING_ID_NAME;
if (s == "description") return OBJ_STRING_ID_DESCRIPTION;
if (s == "capacity") return OBJ_STRING_ID_CAPACITY;
if (s == "vehicleName") return OBJ_STRING_ID_VEHICLE_NAME;
return OBJ_STRING_ID_UNKNOWN;
}
void LoadStrings(const json_t * root, StringTable &stringTable)
{
auto jsonStrings = json_object_get(root, "strings");
const char * key;
json_t * jlanguages;
json_object_foreach(jsonStrings, key, jlanguages)
{
auto stringId = ParseStringId(key);
if (stringId != OBJ_STRING_ID_UNKNOWN)
{
const char * locale;
json_t * jstring;
json_object_foreach(jlanguages, locale, jstring)
{
auto langId = language_get_id_from_locale(locale);
if (langId != LANGUAGE_UNDEFINED)
{
auto string = json_string_value(jstring);
stringTable.SetString(stringId, langId, string);
}
}
}
}
stringTable.Sort();
}
void LoadImages(IReadObjectContext * context, const json_t * root, ImageTable &imageTable)
{
if (context->ShouldLoadImages())
{
auto jsonImages = json_object_get(root, "images");
auto imageElements = GetJsonStringArray(jsonImages);
for (const auto &ie : imageElements)
{
auto images = ParseImages(context, ie);
for (const auto &g1 : images)
{
imageTable.AddImage(&g1);
std::free(g1.offset);
}
}
}
}
}

View File

@ -0,0 +1,57 @@
#pragma region Copyright (c) 2014-2018 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#pragma once
#include <initializer_list>
#include <string>
#include <utility>
#include <vector>
#include "../common.h"
#include "../core/Json.hpp"
#include "../drawing/Drawing.h"
#include "../object/Object.h"
#include "ImageTable.h"
#include "StringTable.h"
namespace ObjectJsonHelpers
{
bool GetBoolean(const json_t * obj, const std::string &name, bool defaultValue = false);
std::string GetString(const json_t * value);
std::string GetString(const json_t * obj, const std::string &name, const std::string &defaultValue = "");
sint32 GetInteger(const json_t * obj, const std::string &name, const sint32 &defaultValue = 0);
float GetFloat(const json_t * obj, const std::string &name, const float &defaultValue = 0);
std::vector<std::string> GetJsonStringArray(const json_t * arr);
std::vector<sint32> GetJsonIntegerArray(const json_t * arr);
uint8 ParseCursor(const std::string &s, uint8 defaultValue);
rct_object_entry ParseObjectEntry(const std::string & s);
void LoadStrings(const json_t * root, StringTable &stringTable);
void LoadImages(IReadObjectContext * context, const json_t * root, ImageTable &imageTable);
template<typename T>
static T GetFlags(const json_t * obj, std::initializer_list<std::pair<std::string, T>> list)
{
T flags = 0;
for (const auto &item : list)
{
if (GetBoolean(obj, item.first))
{
flags |= item.second;
}
}
return flags;
}
};

View File

@ -80,7 +80,7 @@ class ObjectFileIndex final : public FileIndex<ObjectRepositoryItem>
private:
static constexpr uint32 MAGIC_NUMBER = 0x5844494F; // OIDX
static constexpr uint16 VERSION = 17;
static constexpr auto PATTERN = "*.dat;*.pob";
static constexpr auto PATTERN = "*.dat;*.pob;*.json";
public:
explicit ObjectFileIndex(IPlatformEnvironment * env) :
@ -90,7 +90,7 @@ public:
env->GetFilePath(PATHID::CACHE_OBJECTS),
std::string(PATTERN),
std::vector<std::string>({
env->GetDirectoryPath(DIRBASE::RCT2, DIRID::OBJECT),
env->GetDirectoryPath(DIRBASE::OPENRCT2, DIRID::OBJECT),
env->GetDirectoryPath(DIRBASE::USER, DIRID::OBJECT) }))
{
}
@ -98,21 +98,36 @@ public:
public:
std::tuple<bool, ObjectRepositoryItem> Create(const std::string &path) const override
{
auto object = ObjectFactory::CreateObjectFromLegacyFile(path.c_str());
if (object != nullptr)
auto extension = Path::GetExtension(path);
if (String::Equals(extension, ".json", true))
{
ObjectRepositoryItem item = { 0 };
item.ObjectEntry = *object->GetObjectEntry();
item.Path = String::Duplicate(path);
item.Name = String::Duplicate(object->GetName());
object->SetRepositoryItem(&item);
delete object;
return std::make_tuple(true, item);
auto object = ObjectFactory::CreateObjectFromJsonFile(path);
if (object != nullptr)
{
ObjectRepositoryItem item = { 0 };
item.ObjectEntry = *object->GetObjectEntry();
item.Path = String::Duplicate(path);
item.Name = String::Duplicate(object->GetName());
object->SetRepositoryItem(&item);
delete object;
return std::make_tuple(true, item);
}
}
else
{
return std::make_tuple(false, ObjectRepositoryItem());
auto object = ObjectFactory::CreateObjectFromLegacyFile(path.c_str());
if (object != nullptr)
{
ObjectRepositoryItem item = { 0 };
item.ObjectEntry = *object->GetObjectEntry();
item.Path = String::Duplicate(path);
item.Name = String::Duplicate(object->GetName());
object->SetRepositoryItem(&item);
delete object;
return std::make_tuple(true, item);
}
}
return std::make_tuple(false, ObjectRepositoryItem());
}
protected:
@ -260,8 +275,15 @@ public:
{
Guard::ArgumentNotNull(ori, GUARD_LINE);
Object * object = ObjectFactory::CreateObjectFromLegacyFile(ori->Path);
return object;
auto extension = Path::GetExtension(ori->Path);
if (String::Equals(extension, ".json", true))
{
return ObjectFactory::CreateObjectFromJsonFile(ori->Path);
}
else
{
return ObjectFactory::CreateObjectFromLegacyFile(ori->Path);
}
}
void RegisterLoadedObject(const ObjectRepositoryItem * ori, Object * object) override

View File

@ -14,27 +14,27 @@
*****************************************************************************/
#pragma endregion
#pragma warning(disable : 4706) // assignment within conditional expression
#include <algorithm>
#include <unordered_map>
#include "../core/IStream.hpp"
#include "../core/Math.hpp"
#include "../core/Memory.hpp"
#include "../core/String.hpp"
#include "../OpenRCT2.h"
#include "ObjectRepository.h"
#include "RideObject.h"
#include "../core/Util.hpp"
#include "../ride/RideGroupManager.h"
#include "../drawing/Drawing.h"
#include "../localisation/Language.h"
#include "../rct2/RCT2.h"
#include "../ride/Ride.h"
#include "../ride/Track.h"
#include "../OpenRCT2.h"
#include "ObjectJsonHelpers.h"
#include "ObjectRepository.h"
#include "RideObject.h"
RideObject::~RideObject()
{
for (auto &peepLoadingPosition : _peepLoadingPositions)
{
Memory::Free(peepLoadingPosition);
}
}
using namespace OpenRCT2;
void RideObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
{
@ -70,9 +70,9 @@ void RideObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
_legacyType.shop_item = stream->ReadValue<uint8>();
_legacyType.shop_item_secondary = stream->ReadValue<uint8>();
GetStringTable()->Read(context, stream, OBJ_STRING_ID_NAME);
GetStringTable()->Read(context, stream, OBJ_STRING_ID_DESCRIPTION);
GetStringTable()->Read(context, stream, OBJ_STRING_ID_CAPACITY);
GetStringTable().Read(context, stream, OBJ_STRING_ID_NAME);
GetStringTable().Read(context, stream, OBJ_STRING_ID_DESCRIPTION);
GetStringTable().Read(context, stream, OBJ_STRING_ID_CAPACITY);
// Read preset colours, by default there are 32
_presetColours.count = stream->ReadValue<uint8>();
@ -98,11 +98,13 @@ void RideObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
{
numPeepLoadingPositions = stream->ReadValue<uint16>();
}
_peepLoadingPositions[i] = stream->ReadArray<sint8>(numPeepLoadingPositions);
_peepLoadingPositionsCount[i] = numPeepLoadingPositions;
auto data = stream->ReadArray<sint8>(numPeepLoadingPositions);
_peepLoadingPositions[i] = std::vector<sint8>(data, data + numPeepLoadingPositions);
Memory::Free(data);
}
GetImageTable()->Read(context, stream);
GetImageTable().Read(context, stream);
// Validate properties
if (_legacyType.excitement_multiplier > 75)
@ -117,17 +119,17 @@ void RideObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
{
context->LogError(OBJECT_ERROR_INVALID_PROPERTY, "Nausea multiplier too high.");
}
PerformFixes();
}
void RideObject::Load()
{
GetStringTable()->Sort();
_legacyType.obj = this;
GetStringTable().Sort();
_legacyType.naming.name = language_allocate_object_string(GetName());
_legacyType.naming.description = language_allocate_object_string(GetDescription());
_legacyType.capacity = language_allocate_object_string(GetCapacity());
_legacyType.images_offset = gfx_object_allocate_images(GetImageTable()->GetImages(), GetImageTable()->GetCount());
_legacyType.images_offset = gfx_object_allocate_images(GetImageTable().GetImages(), GetImageTable().GetCount());
_legacyType.vehicle_preset_list = &_presetColours;
sint32 cur_vehicle_images_offset = _legacyType.images_offset + MAX_RIDE_TYPES_PER_RIDE_ENTRY;
@ -281,8 +283,11 @@ void RideObject::Load()
set_vehicle_type_image_max_sizes(vehicleEntry, num_images);
}
}
vehicleEntry->peep_loading_positions = _peepLoadingPositions[i];
vehicleEntry->peep_loading_positions_count = _peepLoadingPositionsCount[i];
if (!_peepLoadingPositions[i].empty())
{
vehicleEntry->peep_loading_positions = std::move(_peepLoadingPositions[i]);
}
}
}
}
@ -292,7 +297,7 @@ void RideObject::Unload()
language_free_object_string(_legacyType.naming.name);
language_free_object_string(_legacyType.naming.description);
language_free_object_string(_legacyType.capacity);
gfx_object_free_images(_legacyType.images_offset, GetImageTable()->GetCount());
gfx_object_free_images(_legacyType.images_offset, GetImageTable().GetCount());
_legacyType.naming.name = 0;
_legacyType.naming.description = 0;
@ -371,8 +376,7 @@ void RideObject::SetRepositoryItem(ObjectRepositoryItem * item) const
void RideObject::ReadLegacyVehicle(IReadObjectContext * context, IStream * stream, rct_ride_entry_vehicle * vehicle)
{
vehicle->rotation_frame_mask = stream->ReadValue<uint16>();
vehicle->num_vertical_frames = stream->ReadValue<uint8>();
vehicle->num_horizontal_frames = stream->ReadValue<uint8>();
stream->Seek(2 * 1, STREAM_SEEK_CURRENT);
vehicle->spacing = stream->ReadValue<uint32>();
vehicle->car_mass = stream->ReadValue<uint16>();
vehicle->tab_height = stream->ReadValue<sint8>();
@ -384,21 +388,7 @@ void RideObject::ReadLegacyVehicle(IReadObjectContext * context, IStream * strea
vehicle->animation = stream->ReadValue<uint8>();
vehicle->flags = stream->ReadValue<uint32>();
vehicle->base_num_frames = stream->ReadValue<uint16>();
stream->Seek(4, STREAM_SEEK_CURRENT);
vehicle->restraint_image_id = stream->ReadValue<uint32>();
vehicle->gentle_slope_image_id = stream->ReadValue<uint32>();
vehicle->steep_slope_image_id = stream->ReadValue<uint32>();
vehicle->vertical_slope_image_id = stream->ReadValue<uint32>();
vehicle->diagonal_slope_image_id = stream->ReadValue<uint32>();
vehicle->banked_image_id = stream->ReadValue<uint32>();
vehicle->inline_twist_image_id = stream->ReadValue<uint32>();
vehicle->flat_to_gentle_bank_image_id = stream->ReadValue<uint32>();
vehicle->diagonal_to_gentle_slope_bank_image_id = stream->ReadValue<uint32>();
vehicle->gentle_slope_to_bank_image_id = stream->ReadValue<uint32>();
vehicle->gentle_slope_bank_turn_image_id = stream->ReadValue<uint32>();
vehicle->flat_bank_to_gentle_slope_image_id = stream->ReadValue<uint32>();
vehicle->corkscrew_image_id = stream->ReadValue<uint32>();
vehicle->no_vehicle_images = stream->ReadValue<uint32>();
stream->Seek(15 * 4, STREAM_SEEK_CURRENT);
vehicle->no_seating_rows = stream->ReadValue<uint8>();
vehicle->spinning_inertia = stream->ReadValue<uint8>();
vehicle->spinning_friction = stream->ReadValue<uint8>();
@ -415,54 +405,6 @@ void RideObject::ReadLegacyVehicle(IReadObjectContext * context, IStream * strea
stream->Seek(4, STREAM_SEEK_CURRENT);
}
void RideObject::PerformFixes()
{
std::string identifier = GetIdentifier();
// Add boosters if the track type is eligible
for (auto rideType : _legacyType.ride_type)
{
if (ride_type_supports_boosters(rideType))
{
_legacyType.enabledTrackPieces |= (1ULL << TRACK_BOOSTER);
}
}
// The rocket cars could take 3 cars per train in RCT1. Restore this.
if (String::Equals(identifier, "RCKC "))
{
_legacyType.max_cars_in_train = 3 + _legacyType.zero_cars;
}
// The Wooden Roller Coaster could take 7 cars per train in RCT1.
else if (String::Equals(identifier, "PTCT1 "))
{
_legacyType.max_cars_in_train = 7 + _legacyType.zero_cars;
}
// The Looping Roller Coaster could take 8 cars per train in RCT1.
else if (String::Equals(identifier, "SCHT1 "))
{
_legacyType.max_cars_in_train = 8 + _legacyType.zero_cars;
}
// The Steel Twister could take 8 cars per train in RCT1. (The other two vehicles are already correct.)
else if (String::Equals(identifier, "BMSD ") || String::Equals(identifier, "BMSU "))
{
_legacyType.max_cars_in_train = 8 + _legacyType.zero_cars;
}
// Wacky Worlds' Crocodile Ride (a log flume vehicle) is incorrectly locked to 5 cars.
else if (String::Equals(identifier, "CROCFLUM"))
{
_legacyType.cars_per_flat_ride = 0xFF;
}
// All vanilla/WW/OCC Junior RC vehicles incorrectly have this flag set
else if (String::Equals(identifier, "ZLDB ") ||
String::Equals(identifier, "ZLOG ") ||
String::Equals(identifier, "ZPANDA ") ||
String::Equals(identifier, "WHICGRUB"))
{
_legacyType.enabledTrackPieces &= ~(1ULL << TRACK_SLOPE_STEEP);
}
}
uint8 RideObject::CalculateNumVerticalFrames(const rct_ride_entry_vehicle * vehicleEntry)
{
// 0x6DE90B
@ -532,3 +474,642 @@ uint8 RideObject::CalculateNumHorizontalFrames(const rct_ride_entry_vehicle * ve
return numHorizontalFrames;
}
void RideObject::ReadJson(IReadObjectContext * context, const json_t * root)
{
auto properties = json_object_get(root, "properties");
auto rideTypes = ObjectJsonHelpers::GetJsonStringArray(json_object_get(properties, "type"));
for (size_t i = 0; i < MAX_RIDE_TYPES_PER_RIDE_ENTRY; i++)
{
uint8 rideType = RIDE_TYPE_NULL;
if (i < rideTypes.size())
{
rideType = ParseRideType(rideTypes[i]);
if (rideType == RIDE_TYPE_NULL)
{
context->LogError(OBJECT_ERROR_INVALID_PROPERTY, "Unknown ride type");
}
}
_legacyType.ride_type[i] = rideType;
}
auto rideCategories = ObjectJsonHelpers::GetJsonStringArray(json_object_get(properties, "category"));
if (rideCategories.size() >= 1)
{
_legacyType.category[0] = ParseRideCategory(rideCategories[0]);
_legacyType.category[1] = _legacyType.category[0];
}
if (rideCategories.size() >= 2)
{
_legacyType.category[1] = ParseRideCategory(rideCategories[1]);
}
_legacyType.max_height = ObjectJsonHelpers::GetInteger(properties, "maxHeight");
// This needs to be set for both shops/facilities _and_ regular rides.
_legacyType.shop_item = SHOP_ITEM_NONE;
_legacyType.shop_item_secondary = SHOP_ITEM_NONE;
if (IsRideTypeShopOrFacility(_legacyType.ride_type[0]))
{
// Standard car info for a shop
auto &car = _legacyType.vehicles[0];
car.spacing = 544;
car.sprite_flags = VEHICLE_SPRITE_FLAG_FLAT;
car.sprite_width = 1;
car.sprite_height_negative = 1;
car.sprite_height_positive = 1;
car.flags = VEHICLE_ENTRY_FLAG_SPINNING;
car.car_visual = VEHICLE_VISUAL_FLAT_RIDE_OR_CAR_RIDE;
car.friction_sound_id = 0xFF;
car.sound_range = 0xFF;
car.draw_order = 6;
// Shop item
auto rideSells = ObjectJsonHelpers::GetJsonStringArray(json_object_get(json_object_get(root, "properties"), "sells"));
for (size_t i = 0; i < rideSells.size(); i++)
{
auto shopItem = ParseShopItem(rideSells[i]);
if (shopItem == SHOP_ITEM_NONE)
{
context->LogWarning(OBJECT_ERROR_INVALID_PROPERTY, "Unknown shop item");
}
if (i == 0)
{
_legacyType.shop_item = ParseShopItem(rideSells[0]);
}
else if (i == 1)
{
_legacyType.shop_item_secondary = ParseShopItem(rideSells[1]);
}
else
{
// More than 2 shop items not supported yet!
}
}
}
else
{
ReadJsonVehicleInfo(context, properties);
auto swingMode = ObjectJsonHelpers::GetInteger(properties, "swingMode");
if (swingMode == 1)
{
_legacyType.flags |= RIDE_ENTRY_FLAG_ALTERNATIVE_SWING_MODE_1;
}
else if (swingMode == 2)
{
_legacyType.flags |= RIDE_ENTRY_FLAG_ALTERNATIVE_SWING_MODE_1;
_legacyType.flags |= RIDE_ENTRY_FLAG_ALTERNATIVE_SWING_MODE_2;
}
auto rotationMode = ObjectJsonHelpers::GetInteger(properties, "rotationMode");
if (rotationMode == 1)
{
_legacyType.flags |= RIDE_ENTRY_FLAG_ALTERNATIVE_ROTATION_MODE_1;
}
else if (rotationMode == 2)
{
_legacyType.flags |= RIDE_ENTRY_FLAG_ALTERNATIVE_ROTATION_MODE_2;
}
auto ratingMultiplier = json_object_get(properties, "ratingMultipler");
if (ratingMultiplier != nullptr)
{
_legacyType.excitement_multiplier = ObjectJsonHelpers::GetInteger(ratingMultiplier, "excitement");
_legacyType.intensity_multiplier = ObjectJsonHelpers::GetInteger(ratingMultiplier, "intensity");
_legacyType.nausea_multiplier = ObjectJsonHelpers::GetInteger(ratingMultiplier, "nausea");
}
auto availableTrackPieces = ObjectJsonHelpers::GetJsonStringArray(json_object_get(properties, "availableTrackPieces"));
_presetColours = ReadJsonCarColours(json_object_get(properties, "carColours"));
}
_legacyType.flags |= ObjectJsonHelpers::GetFlags<uint32>(properties, {
{ "noInversions", RIDE_ENTRY_FLAG_NO_INVERSIONS },
{ "noBanking", RIDE_ENTRY_FLAG_NO_BANKED_TRACK },
{ "playDepartSound", RIDE_ENTRY_FLAG_PLAY_DEPART_SOUND },
{ "RIDE_ENTRY_FLAG_7", RIDE_ENTRY_FLAG_7 },
{ "playSplashSound", RIDE_ENTRY_FLAG_PLAY_SPLASH_SOUND },
{ "playSplashSoundSlide", RIDE_ENTRY_FLAG_PLAY_SPLASH_SOUND_SLIDE },
{ "hasShelter", RIDE_ENTRY_FLAG_COVERED_RIDE },
{ "limitAirTimeBonus", RIDE_ENTRY_FLAG_LIMIT_AIRTIME_BONUS },
{ "disableBreakdown", RIDE_ENTRY_FLAG_CANNOT_BREAK_DOWN },
{ "RIDE_ENTRY_FLAG_16", RIDE_ENTRY_FLAG_16 },
{ "RIDE_ENTRY_FLAG_18", RIDE_ENTRY_FLAG_18 },
{ "disablePainting", RIDE_ENTRY_FLAG_DISABLE_COLOUR_TAB } });
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
}
void RideObject::ReadJsonVehicleInfo(IReadObjectContext * context, const json_t * properties)
{
_legacyType.min_cars_in_train = ObjectJsonHelpers::GetInteger(properties, "minCarsPerTrain", 1);
_legacyType.max_cars_in_train = ObjectJsonHelpers::GetInteger(properties, "maxCarsPerTrain", 1);
_legacyType.cars_per_flat_ride = ObjectJsonHelpers::GetInteger(properties, "carsPerFlatRide", 255);
_legacyType.zero_cars = json_integer_value(json_object_get(properties, "numEmptyCars"));
// Train formation from car indices
_legacyType.default_vehicle = json_integer_value(json_object_get(properties, "defaultCar"));
_legacyType.tab_vehicle = json_integer_value(json_object_get(properties, "tabCar"));
auto tabScale = ObjectJsonHelpers::GetFloat(properties, "tabScale");
if (tabScale != 0 && ObjectJsonHelpers::GetFloat(properties, "tabScale") <= 0.5f)
{
_legacyType.flags |= RIDE_ENTRY_FLAG_VEHICLE_TAB_SCALE_HALF;
}
// 0xFF means N/A.
_legacyType.front_vehicle = 0xFF;
_legacyType.second_vehicle = 0xFF;
_legacyType.third_vehicle = 0xFF;
_legacyType.rear_vehicle = 0xFF;
auto headCars = ObjectJsonHelpers::GetJsonIntegerArray(json_object_get(properties, "headCars"));
if (headCars.size() >= 1)
{
_legacyType.front_vehicle = headCars[0];
}
if (headCars.size() >= 2)
{
_legacyType.second_vehicle = headCars[1];
}
if (headCars.size() >= 3)
{
_legacyType.third_vehicle = headCars[2];
}
if (headCars.size() >= 4)
{
// More than 3 head cars not supported yet!
}
auto tailCars = ObjectJsonHelpers::GetJsonIntegerArray(json_object_get(properties, "tailCars"));
if (tailCars.size() >= 1)
{
_legacyType.rear_vehicle = tailCars[0];
}
if (tailCars.size() >= 2)
{
// More than 1 tail car not supported yet!
}
auto cars = ReadJsonCars(json_object_get(properties, "cars"));
auto numCars = std::min(Util::CountOf(_legacyType.vehicles), cars.size());
for (size_t i = 0; i < numCars; i++)
{
_legacyType.vehicles[i] = cars[i];
}
}
std::vector<rct_ride_entry_vehicle> RideObject::ReadJsonCars(const json_t * jCars)
{
std::vector<rct_ride_entry_vehicle> cars;
if (json_is_array(jCars))
{
json_t * jCar;
size_t index;
json_array_foreach(jCars, index, jCar)
{
auto car = ReadJsonCar(jCar);
cars.push_back(car);
}
}
else if (json_is_object(jCars))
{
auto car = ReadJsonCar(jCars);
cars.push_back(car);
}
return cars;
}
rct_ride_entry_vehicle RideObject::ReadJsonCar(const json_t * jCar)
{
rct_ride_entry_vehicle car = { 0 };
car.rotation_frame_mask = ObjectJsonHelpers::GetInteger(jCar, "rotationFrameMask");
car.spacing = ObjectJsonHelpers::GetInteger(jCar, "spacing");
car.car_mass = ObjectJsonHelpers::GetInteger(jCar, "mass");
car.tab_height = ObjectJsonHelpers::GetInteger(jCar, "tabOffset");
car.num_seats = ObjectJsonHelpers::GetInteger(jCar, "numSeats");
if (ObjectJsonHelpers::GetBoolean(jCar, "seatsInPairs"))
{
car.num_seats |= 0x80;
}
car.sprite_width = ObjectJsonHelpers::GetInteger(jCar, "spriteWidth");
car.sprite_height_negative = ObjectJsonHelpers::GetInteger(jCar, "spriteHeightNegative");
car.sprite_height_positive = ObjectJsonHelpers::GetInteger(jCar, "spriteHeightPositive");
car.animation = ObjectJsonHelpers::GetInteger(jCar, "animation");
car.base_num_frames = ObjectJsonHelpers::GetInteger(jCar, "baseNumFrames");
car.no_vehicle_images = ObjectJsonHelpers::GetInteger(jCar, "numImages");
car.no_seating_rows = ObjectJsonHelpers::GetInteger(jCar, "numSeatRows");
car.spinning_inertia = ObjectJsonHelpers::GetInteger(jCar, "spinningInertia");
car.spinning_friction = ObjectJsonHelpers::GetInteger(jCar, "spinningFriction");
car.friction_sound_id = ObjectJsonHelpers::GetInteger(jCar, "frictionSoundId", 255);
car.log_flume_reverser_vehicle_type = ObjectJsonHelpers::GetInteger(jCar, "logFlumeReverserVehicleType");
car.sound_range = ObjectJsonHelpers::GetInteger(jCar, "soundRange", 255);
car.double_sound_frequency = ObjectJsonHelpers::GetInteger(jCar, "doubleSoundFrequency");
car.powered_acceleration = ObjectJsonHelpers::GetInteger(jCar, "poweredAcceleration");
car.powered_max_speed = ObjectJsonHelpers::GetInteger(jCar, "poweredMaxSpeed");
car.car_visual = ObjectJsonHelpers::GetInteger(jCar, "carVisual");
car.effect_visual = ObjectJsonHelpers::GetInteger(jCar, "effectVisual", 1);
car.draw_order = ObjectJsonHelpers::GetInteger(jCar, "drawOrder");
car.num_vertical_frames_override = ObjectJsonHelpers::GetInteger(jCar, "numVerticalFramesOverride");
auto& peepLoadingPositions = car.peep_loading_positions;
auto jLoadingPositions = json_object_get(jCar, "loadingPositions");
if (json_is_array(jLoadingPositions))
{
auto arr = ObjectJsonHelpers::GetJsonIntegerArray(jLoadingPositions);
for (auto x : arr)
{
peepLoadingPositions.push_back(x);
}
}
else
{
auto jLoadingWaypoints = json_object_get(jCar, "loadingWaypoints");
if (json_is_array(jLoadingWaypoints))
{
car.flags |= VEHICLE_ENTRY_FLAG_26;
auto numSegments = ObjectJsonHelpers::GetInteger(jCar, "numSegments");
if (numSegments == 8)
{
peepLoadingPositions.push_back(1);
}
else if (numSegments == 4)
{
peepLoadingPositions.push_back(1);
}
else
{
peepLoadingPositions.push_back(0);
}
size_t i;
json_t * route;
json_array_foreach(jLoadingWaypoints, i, route)
{
if (json_is_array(route))
{
size_t j;
json_t * waypoint;
json_array_foreach(route, j, waypoint)
{
if (json_is_array(waypoint) && json_array_size(waypoint) >= 2)
{
auto x = json_integer_value(json_array_get(waypoint, 0));
auto y = json_integer_value(json_array_get(waypoint, 1));
peepLoadingPositions.push_back(x);
peepLoadingPositions.push_back(y);
}
}
peepLoadingPositions.push_back(0);
peepLoadingPositions.push_back(0);
}
}
}
}
auto jFrames = json_object_get(jCar, "frames");
car.sprite_flags = ObjectJsonHelpers::GetFlags<uint16>(jFrames, {
{ "flat", VEHICLE_SPRITE_FLAG_FLAT },
{ "gentleSlopes", VEHICLE_SPRITE_FLAG_GENTLE_SLOPES },
{ "steepSlopes", VEHICLE_SPRITE_FLAG_STEEP_SLOPES },
{ "verticalSlopes", VEHICLE_SPRITE_FLAG_VERTICAL_SLOPES },
{ "diagonalSlopes", VEHICLE_SPRITE_FLAG_DIAGONAL_SLOPES },
{ "flatBanked", VEHICLE_SPRITE_FLAG_FLAT_BANKED },
{ "inlineTwists", VEHICLE_SPRITE_FLAG_INLINE_TWISTS },
{ "flatToGentleSlopeBankedTransitions", VEHICLE_SPRITE_FLAG_FLAT_TO_GENTLE_SLOPE_BANKED_TRANSITIONS },
{ "diagonalGentleSlopeBankedTransitions", VEHICLE_SPRITE_FLAG_DIAGONAL_GENTLE_SLOPE_BANKED_TRANSITIONS },
{ "gentleSlopeBankedTransitions", VEHICLE_SPRITE_FLAG_GENTLE_SLOPE_BANKED_TRANSITIONS },
{ "gentleSlopeBankedTurns", VEHICLE_SPRITE_FLAG_GENTLE_SLOPE_BANKED_TURNS },
{ "flatToGentleSlopeWhileBankedTransitions", VEHICLE_SPRITE_FLAG_FLAT_TO_GENTLE_SLOPE_WHILE_BANKED_TRANSITIONS },
{ "corkscrews", VEHICLE_SPRITE_FLAG_CORKSCREWS },
{ "restraintAnimation", VEHICLE_SPRITE_FLAG_RESTRAINT_ANIMATION },
{ "curvedLiftHill", VEHICLE_SPRITE_FLAG_CURVED_LIFT_HILL },
{ "VEHICLE_SPRITE_FLAG_15", VEHICLE_SPRITE_FLAG_15 } });
car.flags |= ObjectJsonHelpers::GetFlags<uint32>(jCar, {
{ "VEHICLE_ENTRY_FLAG_POWERED_RIDE_UNRESTRICTED_GRAVITY", VEHICLE_ENTRY_FLAG_POWERED_RIDE_UNRESTRICTED_GRAVITY },
{ "VEHICLE_ENTRY_FLAG_NO_UPSTOP_WHEELS", VEHICLE_ENTRY_FLAG_NO_UPSTOP_WHEELS },
{ "VEHICLE_ENTRY_FLAG_NO_UPSTOP_BOBSLEIGH", VEHICLE_ENTRY_FLAG_NO_UPSTOP_BOBSLEIGH },
{ "VEHICLE_ENTRY_FLAG_MINI_GOLF", VEHICLE_ENTRY_FLAG_MINI_GOLF },
{ "VEHICLE_ENTRY_FLAG_4", VEHICLE_ENTRY_FLAG_4 },
{ "VEHICLE_ENTRY_FLAG_5", VEHICLE_ENTRY_FLAG_5 },
{ "VEHICLE_ENTRY_FLAG_HAS_INVERTED_SPRITE_SET", VEHICLE_ENTRY_FLAG_HAS_INVERTED_SPRITE_SET },
{ "VEHICLE_ENTRY_FLAG_DODGEM_INUSE_LIGHTS", VEHICLE_ENTRY_FLAG_DODGEM_INUSE_LIGHTS },
{ "VEHICLE_ENTRY_FLAG_ALLOW_DOORS_DEPRECATED", VEHICLE_ENTRY_FLAG_ALLOW_DOORS_DEPRECATED },
{ "VEHICLE_ENTRY_FLAG_ENABLE_ADDITIONAL_COLOUR_2", VEHICLE_ENTRY_FLAG_ENABLE_ADDITIONAL_COLOUR_2 },
{ "VEHICLE_ENTRY_FLAG_10", VEHICLE_ENTRY_FLAG_10 },
{ "VEHICLE_ENTRY_FLAG_11", VEHICLE_ENTRY_FLAG_11 },
{ "VEHICLE_ENTRY_FLAG_OVERRIDE_NUM_VERTICAL_FRAMES", VEHICLE_ENTRY_FLAG_OVERRIDE_NUM_VERTICAL_FRAMES },
{ "VEHICLE_ENTRY_FLAG_13", VEHICLE_ENTRY_FLAG_13 },
{ "VEHICLE_ENTRY_FLAG_SPINNING_ADDITIONAL_FRAMES", VEHICLE_ENTRY_FLAG_SPINNING_ADDITIONAL_FRAMES },
{ "VEHICLE_ENTRY_FLAG_15", VEHICLE_ENTRY_FLAG_15 },
{ "VEHICLE_ENTRY_FLAG_ENABLE_ADDITIONAL_COLOUR_1", VEHICLE_ENTRY_FLAG_ENABLE_ADDITIONAL_COLOUR_1 },
{ "VEHICLE_ENTRY_FLAG_SWINGING", VEHICLE_ENTRY_FLAG_SWINGING },
{ "VEHICLE_ENTRY_FLAG_SPINNING", VEHICLE_ENTRY_FLAG_SPINNING },
{ "VEHICLE_ENTRY_FLAG_POWERED", VEHICLE_ENTRY_FLAG_POWERED },
{ "VEHICLE_ENTRY_FLAG_RIDERS_SCREAM", VEHICLE_ENTRY_FLAG_RIDERS_SCREAM },
{ "VEHICLE_ENTRY_FLAG_21", VEHICLE_ENTRY_FLAG_21 },
{ "VEHICLE_ENTRY_FLAG_BOAT_HIRE_COLLISION_DETECTION", VEHICLE_ENTRY_FLAG_BOAT_HIRE_COLLISION_DETECTION },
{ "VEHICLE_ENTRY_FLAG_VEHICLE_ANIMATION", VEHICLE_ENTRY_FLAG_VEHICLE_ANIMATION },
{ "VEHICLE_ENTRY_FLAG_RIDER_ANIMATION", VEHICLE_ENTRY_FLAG_RIDER_ANIMATION },
{ "VEHICLE_ENTRY_FLAG_25", VEHICLE_ENTRY_FLAG_25 },
{ "VEHICLE_ENTRY_FLAG_SLIDE_SWING", VEHICLE_ENTRY_FLAG_SLIDE_SWING },
{ "VEHICLE_ENTRY_FLAG_CHAIRLIFT", VEHICLE_ENTRY_FLAG_CHAIRLIFT },
{ "VEHICLE_ENTRY_FLAG_WATER_RIDE", VEHICLE_ENTRY_FLAG_WATER_RIDE },
{ "VEHICLE_ENTRY_FLAG_GO_KART", VEHICLE_ENTRY_FLAG_GO_KART },
{ "VEHICLE_ENTRY_FLAG_DODGEM_CAR_PLACEMENT", VEHICLE_ENTRY_FLAG_DODGEM_CAR_PLACEMENT } });
return car;
}
vehicle_colour_preset_list RideObject::ReadJsonCarColours(const json_t * jCarColours)
{
// The JSON supports multiple configurations of per car colours, but
// the ride entry structure currently doesn't allow for it. Assume that
// a single configuration with multiple colour entries is per car scheme.
if (json_array_size(jCarColours) == 1)
{
auto firstElement = json_array_get(jCarColours, 0);
auto numColours = json_array_size(firstElement);
if (numColours >= 2)
{
// Read all colours from first config
auto config = ReadJsonColourConfiguration(firstElement);
vehicle_colour_preset_list list = { 0 };
list.count = 255;
std::copy_n(config.data(), std::min<size_t>(numColours, 32), list.list);
return list;
}
}
// Read first colour for each config
vehicle_colour_preset_list list = { 0 };
size_t index;
const json_t * jConfiguration;
json_array_foreach(jCarColours, index, jConfiguration)
{
auto config = ReadJsonColourConfiguration(jConfiguration);
if (config.size() >= 1)
{
list.list[index] = config[0];
list.count++;
if (list.count == 254)
{
// Reached maximum number of configurations
break;
}
}
}
return list;
}
std::vector<vehicle_colour> RideObject::ReadJsonColourConfiguration(const json_t * jColourConfig)
{
std::vector<vehicle_colour> config;
size_t index;
const json_t * jColours;
json_array_foreach(jColourConfig, index, jColours)
{
vehicle_colour carColour = { 0 };
auto colours = ObjectJsonHelpers::GetJsonStringArray(jColours);
if (colours.size() >= 1)
{
carColour.main = ParseColour(colours[0]);
carColour.additional_1 = carColour.main;
carColour.additional_2 = carColour.main;
if (colours.size() >= 2)
{
carColour.additional_1 = ParseColour(colours[1]);
}
if (colours.size() >= 3)
{
carColour.additional_2 = ParseColour(colours[2]);
}
}
config.push_back(carColour);
}
return config;
}
bool RideObject::IsRideTypeShopOrFacility(uint8 rideType)
{
switch (rideType)
{
case RIDE_TYPE_TOILETS:
case RIDE_TYPE_SHOP:
case RIDE_TYPE_DRINK_STALL:
case RIDE_TYPE_FOOD_STALL:
case RIDE_TYPE_INFORMATION_KIOSK:
case RIDE_TYPE_CASH_MACHINE:
case RIDE_TYPE_FIRST_AID:
return true;
default:
return false;
}
}
uint8 RideObject::ParseRideType(const std::string &s)
{
static const std::unordered_map<std::string, uint8> LookupTable
{
{ "spiral_rc", RIDE_TYPE_SPIRAL_ROLLER_COASTER },
{ "stand_up_rc", RIDE_TYPE_STAND_UP_ROLLER_COASTER },
{ "suspended_swinging_rc", RIDE_TYPE_SUSPENDED_SWINGING_COASTER },
{ "inverted_rc", RIDE_TYPE_INVERTED_ROLLER_COASTER },
{ "junior_rc", RIDE_TYPE_JUNIOR_ROLLER_COASTER },
{ "miniature_railway", RIDE_TYPE_MINIATURE_RAILWAY },
{ "monorail", RIDE_TYPE_MONORAIL },
{ "mini_suspended_rc", RIDE_TYPE_MINI_SUSPENDED_COASTER },
{ "boat_hire", RIDE_TYPE_BOAT_HIRE },
{ "wooden_wild_mouse", RIDE_TYPE_WOODEN_WILD_MOUSE },
{ "steeplechase", RIDE_TYPE_STEEPLECHASE },
{ "car_ride", RIDE_TYPE_CAR_RIDE },
{ "launched_freefall", RIDE_TYPE_LAUNCHED_FREEFALL },
{ "bobsleigh_rc", RIDE_TYPE_BOBSLEIGH_COASTER },
{ "observation_tower", RIDE_TYPE_OBSERVATION_TOWER },
{ "looping_rc", RIDE_TYPE_LOOPING_ROLLER_COASTER },
{ "dinghy_slide", RIDE_TYPE_DINGHY_SLIDE },
{ "mine_train_rc", RIDE_TYPE_MINE_TRAIN_COASTER },
{ "chairlift", RIDE_TYPE_CHAIRLIFT },
{ "corkscrew_rc", RIDE_TYPE_CORKSCREW_ROLLER_COASTER },
{ "maze", RIDE_TYPE_MAZE },
{ "spiral_slide", RIDE_TYPE_SPIRAL_SLIDE },
{ "go_karts", RIDE_TYPE_GO_KARTS },
{ "log_flume", RIDE_TYPE_LOG_FLUME },
{ "river_rapids", RIDE_TYPE_RIVER_RAPIDS },
{ "dodgems", RIDE_TYPE_DODGEMS },
{ "swinging_ship", RIDE_TYPE_SWINGING_SHIP },
{ "swinging_inverter_ship", RIDE_TYPE_SWINGING_INVERTER_SHIP },
{ "food_stall", RIDE_TYPE_FOOD_STALL },
{ "drink_stall", RIDE_TYPE_DRINK_STALL },
{ "shop", RIDE_TYPE_SHOP },
{ "merry_go_round", RIDE_TYPE_MERRY_GO_ROUND },
{ "information_kiosk", RIDE_TYPE_INFORMATION_KIOSK },
{ "toilets", RIDE_TYPE_TOILETS },
{ "ferris_wheel", RIDE_TYPE_FERRIS_WHEEL },
{ "motion_simulator", RIDE_TYPE_MOTION_SIMULATOR },
{ "3d_cinema", RIDE_TYPE_3D_CINEMA },
{ "top_spin", RIDE_TYPE_TOP_SPIN },
{ "space_rings", RIDE_TYPE_SPACE_RINGS },
{ "reverse_freefall_rc", RIDE_TYPE_REVERSE_FREEFALL_COASTER },
{ "lift", RIDE_TYPE_LIFT },
{ "vertical_drop_rc", RIDE_TYPE_VERTICAL_DROP_ROLLER_COASTER },
{ "cash_machine", RIDE_TYPE_CASH_MACHINE },
{ "twist", RIDE_TYPE_TWIST },
{ "haunted_house", RIDE_TYPE_HAUNTED_HOUSE },
{ "first_aid", RIDE_TYPE_FIRST_AID },
{ "circus", RIDE_TYPE_CIRCUS },
{ "ghost_train", RIDE_TYPE_GHOST_TRAIN },
{ "twister_rc", RIDE_TYPE_TWISTER_ROLLER_COASTER },
{ "wooden_rc", RIDE_TYPE_WOODEN_ROLLER_COASTER },
{ "side_friction_rc", RIDE_TYPE_SIDE_FRICTION_ROLLER_COASTER },
{ "steel_wild_mouse", RIDE_TYPE_STEEL_WILD_MOUSE },
{ "multi_dimension_rc", RIDE_TYPE_MULTI_DIMENSION_ROLLER_COASTER },
{ "flying_rc", RIDE_TYPE_FLYING_ROLLER_COASTER },
{ "virginia_reel", RIDE_TYPE_VIRGINIA_REEL },
{ "splash_boats", RIDE_TYPE_SPLASH_BOATS },
{ "mini_helicopters", RIDE_TYPE_MINI_HELICOPTERS },
{ "lay_down_rc", RIDE_TYPE_LAY_DOWN_ROLLER_COASTER },
{ "suspended_monorail", RIDE_TYPE_SUSPENDED_MONORAIL },
{ "reverser_rc", RIDE_TYPE_REVERSER_ROLLER_COASTER },
{ "heartline_twister_rc", RIDE_TYPE_HEARTLINE_TWISTER_COASTER },
{ "mini_golf", RIDE_TYPE_MINI_GOLF },
{ "giga_rc", RIDE_TYPE_GIGA_COASTER },
{ "roto_drop", RIDE_TYPE_ROTO_DROP },
{ "flying_saucers", RIDE_TYPE_FLYING_SAUCERS },
{ "crooked_house", RIDE_TYPE_CROOKED_HOUSE },
{ "monorail_cycles", RIDE_TYPE_MONORAIL_CYCLES },
{ "compact_inverted_rc", RIDE_TYPE_COMPACT_INVERTED_COASTER },
{ "water_coaster", RIDE_TYPE_WATER_COASTER },
{ "air_powered_vertical_rc", RIDE_TYPE_AIR_POWERED_VERTICAL_COASTER },
{ "inverted_hairpin_rc", RIDE_TYPE_INVERTED_HAIRPIN_COASTER },
{ "magic_carpet", RIDE_TYPE_MAGIC_CARPET },
{ "submarine_ride", RIDE_TYPE_SUBMARINE_RIDE },
{ "river_rafts", RIDE_TYPE_RIVER_RAFTS },
{ "enterprise", RIDE_TYPE_ENTERPRISE },
{ "inverted_impulse_rc", RIDE_TYPE_INVERTED_IMPULSE_COASTER },
{ "mini_rc", RIDE_TYPE_MINI_ROLLER_COASTER },
{ "mine_ride", RIDE_TYPE_MINE_RIDE },
{ "lim_launched_rc", RIDE_TYPE_LIM_LAUNCHED_ROLLER_COASTER },
};
auto result = LookupTable.find(s);
return (result != LookupTable.end()) ?
result->second :
RIDE_TYPE_NULL;
}
uint8 RideObject::ParseRideCategory(const std::string &s)
{
static const std::unordered_map<std::string, uint8> LookupTable
{
{ "transport", RIDE_CATEGORY_TRANSPORT },
{ "gentle", RIDE_CATEGORY_GENTLE },
{ "rollercoaster", RIDE_CATEGORY_ROLLERCOASTER },
{ "thrill", RIDE_CATEGORY_THRILL },
{ "water", RIDE_CATEGORY_WATER },
{ "stall", RIDE_CATEGORY_SHOP },
};
auto result = LookupTable.find(s);
return (result != LookupTable.end()) ?
result->second :
RIDE_CATEGORY_TRANSPORT;
}
uint8 RideObject::ParseShopItem(const std::string &s)
{
static const std::unordered_map<std::string, uint8> LookupTable
{
{ "burger", SHOP_ITEM_BURGER },
{ "chips", SHOP_ITEM_CHIPS },
{ "ice_cream", SHOP_ITEM_ICE_CREAM },
{ "candyfloss", SHOP_ITEM_CANDYFLOSS },
{ "pizza", SHOP_ITEM_PIZZA },
{ "popcorn", SHOP_ITEM_POPCORN },
{ "hot_dog", SHOP_ITEM_HOT_DOG },
{ "tentacle", SHOP_ITEM_TENTACLE },
{ "toffee_apple", SHOP_ITEM_TOFFEE_APPLE },
{ "doughnut", SHOP_ITEM_DOUGHNUT },
{ "chicken", SHOP_ITEM_CHICKEN },
{ "pretzel", SHOP_ITEM_PRETZEL },
{ "funnel_cake", SHOP_ITEM_FUNNEL_CAKE },
{ "beef_noodles", SHOP_ITEM_BEEF_NOODLES },
{ "fried_rice_noodles", SHOP_ITEM_FRIED_RICE_NOODLES },
{ "wonton_soup", SHOP_ITEM_WONTON_SOUP },
{ "meatball_soup", SHOP_ITEM_MEATBALL_SOUP },
{ "sub_sandwich", SHOP_ITEM_SUB_SANDWICH },
{ "cookie", SHOP_ITEM_COOKIE },
{ "roast_sausage", SHOP_ITEM_ROAST_SAUSAGE },
{ "drink", SHOP_ITEM_DRINK },
{ "coffee", SHOP_ITEM_COFFEE },
{ "lemonade", SHOP_ITEM_LEMONADE },
{ "chocolate", SHOP_ITEM_CHOCOLATE },
{ "iced_tea", SHOP_ITEM_ICED_TEA },
{ "fruit_juice", SHOP_ITEM_FRUIT_JUICE },
{ "soybean_milk", SHOP_ITEM_SOYBEAN_MILK },
{ "sujeonggwa", SHOP_ITEM_SUJEONGGWA },
{ "balloon", SHOP_ITEM_BALLOON },
{ "toy", SHOP_ITEM_TOY },
{ "map", SHOP_ITEM_MAP },
{ "photo", SHOP_ITEM_PHOTO },
{ "umbrella", SHOP_ITEM_UMBRELLA },
{ "voucher", SHOP_ITEM_VOUCHER },
{ "hat", SHOP_ITEM_HAT },
{ "tshirt", SHOP_ITEM_TSHIRT },
{ "sunglasses", SHOP_ITEM_SUNGLASSES },
};
auto result = LookupTable.find(s);
return (result != LookupTable.end()) ?
result->second :
SHOP_ITEM_NONE;
}
colour_t RideObject::ParseColour(const std::string &s)
{
static const std::unordered_map<std::string, colour_t> LookupTable
{
{ "black", COLOUR_BLACK },
{ "grey", COLOUR_GREY },
{ "white", COLOUR_WHITE },
{ "dark_purple", COLOUR_DARK_PURPLE },
{ "light_purple", COLOUR_LIGHT_PURPLE },
{ "bright_purple", COLOUR_BRIGHT_PURPLE },
{ "dark_blue", COLOUR_DARK_BLUE },
{ "light_blue", COLOUR_LIGHT_BLUE },
{ "icy_blue", COLOUR_ICY_BLUE },
{ "teal", COLOUR_TEAL },
{ "aquamarine", COLOUR_AQUAMARINE },
{ "saturated_green", COLOUR_SATURATED_GREEN },
{ "dark_green", COLOUR_DARK_GREEN },
{ "moss_green", COLOUR_MOSS_GREEN },
{ "bright_green", COLOUR_BRIGHT_GREEN },
{ "olive_green", COLOUR_OLIVE_GREEN },
{ "dark_olive_green", COLOUR_DARK_OLIVE_GREEN },
{ "bright_yellow", COLOUR_BRIGHT_YELLOW },
{ "yellow", COLOUR_YELLOW },
{ "dark_yellow", COLOUR_DARK_YELLOW },
{ "light_orange", COLOUR_LIGHT_ORANGE },
{ "dark_orange", COLOUR_DARK_ORANGE },
{ "light_brown", COLOUR_LIGHT_BROWN },
{ "saturated_brown", COLOUR_SATURATED_BROWN },
{ "dark_brown", COLOUR_DARK_BROWN },
{ "salmon_pink", COLOUR_SALMON_PINK },
{ "bordeaux_red", COLOUR_BORDEAUX_RED },
{ "saturated_red", COLOUR_SATURATED_RED },
{ "bright_red", COLOUR_BRIGHT_RED },
{ "dark_pink", COLOUR_DARK_PINK },
{ "bright_pink", COLOUR_BRIGHT_PINK },
{ "light_pink", COLOUR_LIGHT_PINK },
};
auto result = LookupTable.find(s);
return (result != LookupTable.end()) ?
result->second :
COLOUR_BLACK;
}

View File

@ -16,24 +16,23 @@
#pragma once
#include "Object.h"
#include <vector>
#include "../ride/Ride.h"
#include "Object.h"
class RideObject final : public Object
{
private:
rct_ride_entry _legacyType = { };
vehicle_colour_preset_list _presetColours = { 0 };
sint8 * _peepLoadingPositions[4] = { nullptr };
uint16 _peepLoadingPositionsCount[4] = { 0 };
std::vector<sint8> _peepLoadingPositions[MAX_VEHICLES_PER_RIDE_ENTRY];
public:
explicit RideObject(const rct_object_entry &entry) : Object(entry) { }
~RideObject();
void * GetLegacyData() override { return &_legacyType; }
void ReadJson(IReadObjectContext * context, const json_t * root) override;
void ReadLegacy(IReadObjectContext * context, IStream * stream) override;
void Load() override;
void Unload() override;
@ -47,8 +46,19 @@ public:
private:
void ReadLegacyVehicle(IReadObjectContext * context, IStream * stream, rct_ride_entry_vehicle * vehicle);
void PerformFixes();
void ReadJsonVehicleInfo(IReadObjectContext * context, const json_t * properties);
std::vector<rct_ride_entry_vehicle> ReadJsonCars(const json_t * jCars);
rct_ride_entry_vehicle ReadJsonCar(const json_t * jCar);
vehicle_colour_preset_list ReadJsonCarColours(const json_t * jCarColours);
std::vector<vehicle_colour> ReadJsonColourConfiguration(const json_t * jColourConfig);
static uint8 CalculateNumVerticalFrames(const rct_ride_entry_vehicle * vehicleEntry);
static uint8 CalculateNumHorizontalFrames(const rct_ride_entry_vehicle * vehicleEntry);
static bool IsRideTypeShopOrFacility(uint8 rideType);
static uint8 ParseRideType(const std::string &s);
static uint8 ParseRideCategory(const std::string &s);
static uint8 ParseShopItem(const std::string &s);
static colour_t ParseColour(const std::string &s);
};

View File

@ -14,15 +14,20 @@
*****************************************************************************/
#pragma endregion
#pragma warning(disable : 4706) // assignment within conditional expression
#include <unordered_map>
#include "../core/IStream.hpp"
#include "../core/Memory.hpp"
#include "../core/String.hpp"
#include "../drawing/Drawing.h"
#include "../localisation/Language.h"
#include "../peep/Staff.h"
#include "ObjectJsonHelpers.h"
#include "ObjectManager.h"
#include "ObjectRepository.h"
#include "SceneryGroupObject.h"
#include "../drawing/Drawing.h"
#include "../localisation/Language.h"
void SceneryGroupObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
{
stream->Seek(6, STREAM_SEEK_CURRENT);
@ -33,23 +38,23 @@ void SceneryGroupObject::ReadLegacy(IReadObjectContext * context, IStream * stre
_legacyType.pad_109 = stream->ReadValue<uint8>();
_legacyType.entertainer_costumes = stream->ReadValue<uint32>();
GetStringTable()->Read(context, stream, OBJ_STRING_ID_NAME);
GetStringTable().Read(context, stream, OBJ_STRING_ID_NAME);
_items = ReadItems(stream);
GetImageTable()->Read(context, stream);
GetImageTable().Read(context, stream);
}
void SceneryGroupObject::Load()
{
GetStringTable()->Sort();
GetStringTable().Sort();
_legacyType.name = language_allocate_object_string(GetName());
_legacyType.image = gfx_object_allocate_images(GetImageTable()->GetImages(), GetImageTable()->GetCount());
_legacyType.image = gfx_object_allocate_images(GetImageTable().GetImages(), GetImageTable().GetCount());
_legacyType.entry_count = 0;
}
void SceneryGroupObject::Unload()
{
language_free_object_string(_legacyType.name);
gfx_object_free_images(_legacyType.image, GetImageTable()->GetCount());
gfx_object_free_images(_legacyType.image, GetImageTable().GetCount());
_legacyType.name = 0;
_legacyType.image = 0;
@ -79,17 +84,20 @@ void SceneryGroupObject::UpdateEntryIndexes()
uint16 sceneryEntry = objectManager->GetLoadedObjectEntryIndex(ori->LoadedObject);
Guard::Assert(sceneryEntry != UINT8_MAX, GUARD_LINE);
uint8 objectType = objectEntry.flags & 0x0F;
auto objectType = ori->ObjectEntry.flags & 0x0F;
switch (objectType) {
case OBJECT_TYPE_SMALL_SCENERY: break;
case OBJECT_TYPE_LARGE_SCENERY: sceneryEntry |= 0x300; break;
case OBJECT_TYPE_WALLS: sceneryEntry |= 0x200; break;
case OBJECT_TYPE_PATH_BITS: sceneryEntry |= 0x100; break;
default: sceneryEntry |= 0x400; break;
case OBJECT_TYPE_WALLS: sceneryEntry |= 0x200; break;
case OBJECT_TYPE_LARGE_SCENERY: sceneryEntry |= 0x300; break;
case OBJECT_TYPE_BANNERS: sceneryEntry |= 0x400; break;
default: sceneryEntry = 0xFFFF; break;
}
if (sceneryEntry != 0xFFFF)
{
_legacyType.scenery_entries[_legacyType.entry_count] = sceneryEntry;
_legacyType.entry_count++;
}
_legacyType.scenery_entries[_legacyType.entry_count] = sceneryEntry;
_legacyType.entry_count++;
}
}
@ -111,8 +119,80 @@ std::vector<rct_object_entry> SceneryGroupObject::ReadItems(IStream * stream)
while (stream->ReadValue<uint8>() != 0xFF)
{
stream->Seek(-1, STREAM_SEEK_CURRENT);
rct_object_entry entry = stream->ReadValue<rct_object_entry>();
auto entry = stream->ReadValue<rct_object_entry>();
items.push_back(entry);
}
return items;
}
void SceneryGroupObject::ReadJson(IReadObjectContext * context, const json_t * root)
{
auto properties = json_object_get(root, "properties");
_legacyType.priority = json_integer_value(json_object_get(properties, "priority"));
// Entertainer cosumes
auto jCostumes = json_object_get(properties, "entertainerCostumes");
if (jCostumes != nullptr)
{
_legacyType.entertainer_costumes = ReadJsonEntertainerCostumes(jCostumes);
}
auto jEntries = json_object_get(properties, "entries");
if (jEntries != nullptr)
{
_items = ReadJsonEntries(jEntries);
}
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
}
uint32 SceneryGroupObject::ReadJsonEntertainerCostumes(const json_t * jCostumes)
{
uint32 costumes = 0;
size_t index;
json_t * jCostume;
json_array_foreach(jCostumes, index, jCostume)
{
auto costume = ObjectJsonHelpers::GetString(jCostume);
auto entertainer = ParseEntertainerCostume(costume);
// For some reason the flags are +4 from the actual costume IDs
// See staff_get_available_entertainer_costumes
costumes |= 1 << (entertainer + 4);
}
return costumes;
}
uint32 SceneryGroupObject::ParseEntertainerCostume(const std::string &s)
{
if (s == "panda") return ENTERTAINER_COSTUME_PANDA;
if (s == "tiger") return ENTERTAINER_COSTUME_TIGER;
if (s == "elephant") return ENTERTAINER_COSTUME_ELEPHANT;
if (s == "roman") return ENTERTAINER_COSTUME_ROMAN;
if (s == "gorilla") return ENTERTAINER_COSTUME_GORILLA;
if (s == "snowman") return ENTERTAINER_COSTUME_SNOWMAN;
if (s == "knight") return ENTERTAINER_COSTUME_KNIGHT;
if (s == "astronaut") return ENTERTAINER_COSTUME_ASTRONAUT;
if (s == "bandit") return ENTERTAINER_COSTUME_BANDIT;
if (s == "sheriff") return ENTERTAINER_COSTUME_SHERIFF;
if (s == "pirate") return ENTERTAINER_COSTUME_PIRATE;
return ENTERTAINER_COSTUME_PANDA;
}
std::vector<rct_object_entry> SceneryGroupObject::ReadJsonEntries(const json_t * jEntries)
{
std::vector<rct_object_entry> entries;
size_t index;
json_t * jEntry;
json_array_foreach(jEntries, index, jEntry)
{
auto entryId = json_string_value(jEntry);
if (entryId != nullptr)
{
auto entry = ObjectJsonHelpers::ParseObjectEntry(entryId);
entries.push_back(entry);
}
}
return entries;
}

View File

@ -32,6 +32,7 @@ public:
explicit SceneryGroupObject(const rct_object_entry &entry) : Object(entry) { }
void * GetLegacyData() override { return &_legacyType; }
void ReadJson(IReadObjectContext * context, const json_t * root) override;
void ReadLegacy(IReadObjectContext * context, IStream * stream) override;
void Load() override;
@ -44,4 +45,7 @@ public:
private:
static std::vector<rct_object_entry> ReadItems(IStream * stream);
static uint32 ReadJsonEntertainerCostumes(const json_t * jCostumes);
static uint32 ParseEntertainerCostume(const std::string &s);
static std::vector<rct_object_entry> ReadJsonEntries(const json_t * jEntries);
};

View File

@ -0,0 +1,27 @@
#pragma region Copyright (c) 2014-2017 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#include "ObjectJsonHelpers.h"
#include "SceneryObject.h"
void SceneryObject::SetPrimarySceneryGroup(const std::string &s)
{
if (!s.empty())
{
auto sgEntry = ObjectJsonHelpers::ParseObjectEntry(s);
SetPrimarySceneryGroup(&sgEntry);
}
}

View File

@ -16,6 +16,7 @@
#pragma once
#include <string>
#include "Object.h"
class SceneryObject : public Object
@ -31,4 +32,5 @@ public:
protected:
void SetPrimarySceneryGroup(const rct_object_entry * entry) { _primarySceneryGroupEntry = *entry; }
void SetPrimarySceneryGroup(const std::string &s);
};

View File

@ -14,16 +14,19 @@
*****************************************************************************/
#pragma endregion
#pragma warning(disable : 4706) // assignment within conditional expression
#include "../core/IStream.hpp"
#include "../core/Math.hpp"
#include "../core/Memory.hpp"
#include "../core/String.hpp"
#include "SmallSceneryObject.h"
#include "../drawing/Drawing.h"
#include "../interface/Cursors.h"
#include "../localisation/Language.h"
#include "../world/Scenery.h"
#include "../world/SmallScenery.h"
#include "SmallSceneryObject.h"
#include "ObjectJsonHelpers.h"
void SmallSceneryObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
{
@ -39,7 +42,7 @@ void SmallSceneryObject::ReadLegacy(IReadObjectContext * context, IStream * stre
_legacyType.small_scenery.num_frames = stream->ReadValue<uint16>();
_legacyType.small_scenery.scenery_tab_id = 0xFF;
GetStringTable()->Read(context, stream, OBJ_STRING_ID_NAME);
GetStringTable().Read(context, stream, OBJ_STRING_ID_NAME);
rct_object_entry sgEntry = stream->ReadValue<rct_object_entry>();
SetPrimarySceneryGroup(&sgEntry);
@ -49,7 +52,7 @@ void SmallSceneryObject::ReadLegacy(IReadObjectContext * context, IStream * stre
_frameOffsets = ReadFrameOffsets(stream);
}
GetImageTable()->Read(context, stream);
GetImageTable().Read(context, stream);
// Validate properties
if (_legacyType.small_scenery.price <= 0)
@ -69,9 +72,9 @@ void SmallSceneryObject::ReadLegacy(IReadObjectContext * context, IStream * stre
void SmallSceneryObject::Load()
{
GetStringTable()->Sort();
GetStringTable().Sort();
_legacyType.name = language_allocate_object_string(GetName());
_legacyType.image = gfx_object_allocate_images(GetImageTable()->GetImages(), GetImageTable()->GetCount());
_legacyType.image = gfx_object_allocate_images(GetImageTable().GetImages(), GetImageTable().GetCount());
_legacyType.small_scenery.scenery_tab_id = 0xFF;
@ -86,7 +89,7 @@ void SmallSceneryObject::Load()
void SmallSceneryObject::Unload()
{
language_free_object_string(_legacyType.name);
gfx_object_free_images(_legacyType.image, GetImageTable()->GetCount());
gfx_object_free_images(_legacyType.image, GetImageTable().GetCount());
_legacyType.name = 0;
_legacyType.image = 0;
@ -231,3 +234,92 @@ rct_object_entry SmallSceneryObject::GetScgAbstrHeader()
{
return Object::CreateHeader("SCGABSTR", 207140231, 932253451);
}
void SmallSceneryObject::ReadJson(IReadObjectContext * context, const json_t * root)
{
auto properties = json_object_get(root, "properties");
_legacyType.small_scenery.height = json_integer_value(json_object_get(properties, "height"));
_legacyType.small_scenery.tool_id = ObjectJsonHelpers::ParseCursor(ObjectJsonHelpers::GetString(properties, "cursor"), CURSOR_STATUE_DOWN);
_legacyType.small_scenery.price = json_integer_value(json_object_get(properties, "price"));
_legacyType.small_scenery.removal_price = json_integer_value(json_object_get(properties, "removalPrice"));
_legacyType.small_scenery.animation_delay = json_integer_value(json_object_get(properties, "animationDelay"));
_legacyType.small_scenery.animation_mask = json_integer_value(json_object_get(properties, "animationMask"));
_legacyType.small_scenery.num_frames = json_integer_value(json_object_get(properties, "numFrames"));
// Flags
_legacyType.small_scenery.flags = ObjectJsonHelpers::GetFlags<uint32>(properties, {
{ "SMALL_SCENERY_FLAG_VOFFSET_CENTRE", SMALL_SCENERY_FLAG_VOFFSET_CENTRE },
{ "requiresFlatSurface", SMALL_SCENERY_FLAG_REQUIRE_FLAT_SURFACE },
{ "isRotatable", SMALL_SCENERY_FLAG_ROTATABLE },
{ "isAnimated", SMALL_SCENERY_FLAG_ANIMATED },
{ "canWither", SMALL_SCENERY_FLAG_CAN_WITHER },
{ "canBeWatered", SMALL_SCENERY_FLAG_CAN_BE_WATERED },
{ "hasOverlayImage", SMALL_SCENERY_FLAG_ANIMATED_FG },
{ "hasGlass", SMALL_SCENERY_FLAG_HAS_GLASS },
{ "hasPrimaryColour", SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR },
{ "SMALL_SCENERY_FLAG_FOUNTAIN_SPRAY_1", SMALL_SCENERY_FLAG_FOUNTAIN_SPRAY_1 },
{ "SMALL_SCENERY_FLAG_FOUNTAIN_SPRAY_4", SMALL_SCENERY_FLAG_FOUNTAIN_SPRAY_4 },
{ "isClock", SMALL_SCENERY_FLAG_IS_CLOCK },
{ "SMALL_SCENERY_FLAG_SWAMP_GOO", SMALL_SCENERY_FLAG_SWAMP_GOO },
{ "SMALL_SCENERY_FLAG17", SMALL_SCENERY_FLAG17 },
{ "isStackable", SMALL_SCENERY_FLAG_STACKABLE },
{ "prohibitWalls", SMALL_SCENERY_FLAG_NO_WALLS },
{ "hasSecondaryColour", SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR },
{ "hasNoSupports", SMALL_SCENERY_FLAG_NO_SUPPORTS },
{ "SMALL_SCENERY_FLAG_VISIBLE_WHEN_ZOOMED", SMALL_SCENERY_FLAG_VISIBLE_WHEN_ZOOMED },
{ "SMALL_SCENERY_FLAG_COG", SMALL_SCENERY_FLAG_COG },
{ "allowSupportsAbove", SMALL_SCENERY_FLAG_BUILD_DIRECTLY_ONTOP },
{ "supportsHavePrimaryColour", SMALL_SCENERY_FLAG_PAINT_SUPPORTS },
{ "SMALL_SCENERY_FLAG27", SMALL_SCENERY_FLAG27 } });
// Determine shape flags from a shape string
auto shape = ObjectJsonHelpers::GetString(properties, "shape");
if (!shape.empty())
{
auto quarters = shape.substr(0, 3);
if (quarters == "2/4")
{
_legacyType.small_scenery.flags |= SMALL_SCENERY_FLAG_FULL_TILE | SMALL_SCENERY_FLAG_HALF_SPACE;
}
else if (quarters == "3/4")
{
_legacyType.small_scenery.flags |= SMALL_SCENERY_FLAG_FULL_TILE | SMALL_SCENERY_FLAG_THREE_QUARTERS;
}
else if (quarters == "4/4")
{
_legacyType.small_scenery.flags |= SMALL_SCENERY_FLAG_FULL_TILE;
}
if (shape.size() >= 5)
{
if ((shape.substr(3) == "+D"))
{
_legacyType.small_scenery.flags |= SMALL_SCENERY_FLAG_DIAGONAL;
}
}
}
auto jFrameOffsets = json_object_get(properties, "frameOffsets");
if (jFrameOffsets != nullptr)
{
_frameOffsets = ReadJsonFrameOffsets(jFrameOffsets);
_legacyType.small_scenery.flags |= SMALL_SCENERY_FLAG_HAS_FRAME_OFFSETS;
}
SetPrimarySceneryGroup(ObjectJsonHelpers::GetString(json_object_get(properties, "sceneryGroup")));
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
}
std::vector<uint8> SmallSceneryObject::ReadJsonFrameOffsets(const json_t * jFrameOffsets)
{
std::vector<uint8> offsets;
size_t index;
const json_t * jOffset;
json_array_foreach(jFrameOffsets, index, jOffset)
{
offsets.push_back(json_integer_value(jOffset));
}
return offsets;
}

View File

@ -32,6 +32,7 @@ public:
void * GetLegacyData() override { return &_legacyType; }
void ReadLegacy(IReadObjectContext * context, IStream * stream) override;
void ReadJson(IReadObjectContext * context, const json_t * root) override;
void Load() override;
void Unload() override;
@ -39,6 +40,7 @@ public:
private:
static std::vector<uint8> ReadFrameOffsets(IStream * stream);
static std::vector<uint8> ReadJsonFrameOffsets(const json_t * jFrameOffsets);
void PerformFixes();
rct_object_entry GetScgPiratHeader();
rct_object_entry GetScgMineHeader();

View File

@ -25,14 +25,14 @@ void StexObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
_legacyType.var_06 = stream->ReadValue<uint8>();
stream->Seek(1, STREAM_SEEK_CURRENT);
GetStringTable()->Read(context, stream, OBJ_STRING_ID_SCENARIO_NAME);
GetStringTable()->Read(context, stream, OBJ_STRING_ID_PARK_NAME);
GetStringTable()->Read(context, stream, OBJ_STRING_ID_SCENARIO_DETAILS);
GetStringTable().Read(context, stream, OBJ_STRING_ID_SCENARIO_NAME);
GetStringTable().Read(context, stream, OBJ_STRING_ID_PARK_NAME);
GetStringTable().Read(context, stream, OBJ_STRING_ID_SCENARIO_DETAILS);
}
void StexObject::Load()
{
GetStringTable()->Sort();
GetStringTable().Sort();
_legacyType.scenario_name = language_allocate_object_string(GetScenarioName());
_legacyType.park_name = language_allocate_object_string(GetParkName());
_legacyType.details = language_allocate_object_string(GetScenarioDetails());
@ -64,15 +64,15 @@ std::string StexObject::GetName() const
std::string StexObject::GetScenarioName() const
{
return GetStringTable()->GetString(OBJ_STRING_ID_SCENARIO_NAME);
return GetStringTable().GetString(OBJ_STRING_ID_SCENARIO_NAME);
}
std::string StexObject::GetScenarioDetails() const
{
return GetStringTable()->GetString(OBJ_STRING_ID_SCENARIO_DETAILS);
return GetStringTable().GetString(OBJ_STRING_ID_SCENARIO_DETAILS);
}
std::string StexObject::GetParkName() const
{
return GetStringTable()->GetString(OBJ_STRING_ID_PARK_NAME);
return GetStringTable().GetString(OBJ_STRING_ID_PARK_NAME);
}

View File

@ -97,7 +97,7 @@ std::string StringTable::GetString(uint8 id) const
return string.Text;
}
}
return nullptr;
return std::string();
}
void StringTable::SetString(uint8 id, uint8 language, const std::string &text)

View File

@ -19,14 +19,27 @@
#include <string>
#include <vector>
#include "../common.h"
#include "../localisation/Language.h"
interface IReadObjectContext;
interface IStream;
enum OBJ_STRING_ID : uint8
{
OBJ_STRING_ID_UNKNOWN = 255,
OBJ_STRING_ID_NAME = 0,
OBJ_STRING_ID_DESCRIPTION,
OBJ_STRING_ID_SCENARIO_NAME = 0,
OBJ_STRING_ID_PARK_NAME = 1,
OBJ_STRING_ID_SCENARIO_DETAILS = 2,
OBJ_STRING_ID_CAPACITY = 2,
OBJ_STRING_ID_VEHICLE_NAME = 3,
};
struct StringTableEntry
{
uint8 Id;
uint8 LanguageId;
uint8 Id = OBJ_STRING_ID_UNKNOWN;
uint8 LanguageId = LANGUAGE_UNDEFINED;
std::string Text;
};
@ -36,6 +49,10 @@ private:
std::vector<StringTableEntry> _strings;
public:
StringTable() = default;
StringTable(const StringTable &) = delete;
StringTable & operator=(const StringTable &) = delete;
void Read(IReadObjectContext * context, IStream * stream, uint8 id);
void Sort();
std::string GetString(uint8 id) const;

View File

@ -15,10 +15,11 @@
#pragma endregion
#include "../core/IStream.hpp"
#include "WallObject.h"
#include "../drawing/Drawing.h"
#include "../interface/Cursors.h"
#include "../localisation/Language.h"
#include "ObjectJsonHelpers.h"
#include "WallObject.h"
void WallObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
{
@ -31,12 +32,12 @@ void WallObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
_legacyType.wall.scenery_tab_id = stream->ReadValue<uint8>();
_legacyType.wall.scrolling_mode = stream->ReadValue<uint8>();
GetStringTable()->Read(context, stream, OBJ_STRING_ID_NAME);
GetStringTable().Read(context, stream, OBJ_STRING_ID_NAME);
rct_object_entry sgEntry = stream->ReadValue<rct_object_entry>();
SetPrimarySceneryGroup(&sgEntry);
GetImageTable()->Read(context, stream);
GetImageTable().Read(context, stream);
// Validate properties
if (_legacyType.wall.price <= 0)
@ -47,15 +48,15 @@ void WallObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
void WallObject::Load()
{
GetStringTable()->Sort();
GetStringTable().Sort();
_legacyType.name = language_allocate_object_string(GetName());
_legacyType.image = gfx_object_allocate_images(GetImageTable()->GetImages(), GetImageTable()->GetCount());
_legacyType.image = gfx_object_allocate_images(GetImageTable().GetImages(), GetImageTable().GetCount());
}
void WallObject::Unload()
{
language_free_object_string(_legacyType.name);
gfx_object_free_images(_legacyType.image, GetImageTable()->GetCount());
gfx_object_free_images(_legacyType.image, GetImageTable().GetCount());
_legacyType.name = 0;
_legacyType.image = 0;
@ -88,3 +89,61 @@ void WallObject::DrawPreview(rct_drawpixelinfo * dpi, sint32 width, sint32 heigh
gfx_draw_sprite(dpi, imageId, x, y, 0);
}
}
void WallObject::ReadJson(IReadObjectContext * context, const json_t * root)
{
auto properties = json_object_get(root, "properties");
_legacyType.wall.tool_id = ObjectJsonHelpers::ParseCursor(ObjectJsonHelpers::GetString(properties, "cursor"), CURSOR_FENCE_DOWN);
_legacyType.wall.height = json_integer_value(json_object_get(properties, "height"));
_legacyType.wall.price = json_integer_value(json_object_get(properties, "price"));
auto jScrollingMode = json_object_get(properties, "scrollingMode");
_legacyType.wall.scrolling_mode = jScrollingMode != nullptr ?
json_integer_value(jScrollingMode) :
-1;
SetPrimarySceneryGroup(ObjectJsonHelpers::GetString(json_object_get(properties, "sceneryGroup")));
// Flags
_legacyType.wall.flags = ObjectJsonHelpers::GetFlags<uint8>(properties, {
{ "hasPrimaryColour", WALL_SCENERY_HAS_PRIMARY_COLOUR },
{ "hasSecondaryColour", WALL_SCENERY_HAS_SECONDARY_COLOUR },
{ "hasTernaryColour", WALL_SCENERY_HAS_TERNARY_COLOUR },
{ "hasGlass", WALL_SCENERY_HAS_GLASS },
{ "isBanner", WALL_SCENERY_IS_BANNER },
{ "isDoor", WALL_SCENERY_IS_DOOR },
{ "isLongDoorAnimation", WALL_SCENERY_LONG_DOOR_ANIMATION }});
_legacyType.wall.flags2 = ObjectJsonHelpers::GetFlags<uint8>(properties, {
{ "isOpaque", WALL_SCENERY_2_IS_OPAQUE },
{ "isAnimated", WALL_SCENERY_2_ANIMATED }});
// HACK To avoid 'negated' properties in JSON, handle this separately until
// flag is inverted in this code base.
if (!ObjectJsonHelpers::GetBoolean(properties, "isAllowedOnSlope", false))
{
_legacyType.wall.flags |= WALL_SCENERY_CANT_BUILD_ON_SLOPE;
}
// HACK WALL_SCENERY_HAS_PRIMARY_COLOUR actually means, has any colour but we simplify the
// JSON and handle this on load. We should change code base in future to reflect the JSON.
if (!(_legacyType.wall.flags & WALL_SCENERY_HAS_PRIMARY_COLOUR))
{
if ((_legacyType.wall.flags & WALL_SCENERY_HAS_SECONDARY_COLOUR) ||
(_legacyType.wall.flags & WALL_SCENERY_HAS_TERNARY_COLOUR))
{
_legacyType.wall.flags2 |= WALL_SCENERY_2_NO_SELECT_PRIMARY_COLOUR;
}
}
// Door sound
auto jDoorSound = json_object_get(properties, "scrollingMode");
if (jDoorSound != nullptr)
{
auto doorSound = json_integer_value(jDoorSound);
_legacyType.wall.flags2 |= (doorSound << WALL_SCENERY_2_DOOR_SOUND_SHIFT) & WALL_SCENERY_2_DOOR_SOUND_MASK;
}
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
ObjectJsonHelpers::LoadImages(context, root, GetImageTable());
}

View File

@ -31,6 +31,7 @@ public:
void * GetLegacyData() override { return &_legacyType; }
void ReadLegacy(IReadObjectContext * context, IStream * stream) override;
void ReadJson(IReadObjectContext * context, const json_t * root) override;
void Load() override;
void Unload() override;

View File

@ -14,10 +14,14 @@
*****************************************************************************/
#pragma endregion
#pragma warning(disable : 4706) // assignment within conditional expression
#include <memory>
#include "../core/IStream.hpp"
#include "../localisation/Language.h"
#include "../localisation/StringIds.h"
#include "../OpenRCT2.h"
#include "ObjectJsonHelpers.h"
#include "WaterObject.h"
void WaterObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
@ -25,15 +29,15 @@ void WaterObject::ReadLegacy(IReadObjectContext * context, IStream * stream)
stream->Seek(14, STREAM_SEEK_CURRENT);
_legacyType.flags = stream->ReadValue<uint16>();
GetStringTable()->Read(context, stream, OBJ_STRING_ID_NAME);
GetImageTable()->Read(context, stream);
GetStringTable().Read(context, stream, OBJ_STRING_ID_NAME);
GetImageTable().Read(context, stream);
}
void WaterObject::Load()
{
GetStringTable()->Sort();
GetStringTable().Sort();
_legacyType.string_idx = language_allocate_object_string(GetName());
_legacyType.image_id = gfx_object_allocate_images(GetImageTable()->GetImages(), GetImageTable()->GetCount());
_legacyType.image_id = gfx_object_allocate_images(GetImageTable().GetImages(), GetImageTable().GetCount());
_legacyType.palette_index_1 = _legacyType.image_id + 1;
_legacyType.palette_index_2 = _legacyType.image_id + 4;
@ -42,7 +46,7 @@ void WaterObject::Load()
void WaterObject::Unload()
{
gfx_object_free_images(_legacyType.image_id, GetImageTable()->GetCount());
gfx_object_free_images(_legacyType.image_id, GetImageTable().GetCount());
language_free_object_string(_legacyType.string_idx);
_legacyType.string_idx = 0;
@ -55,3 +59,85 @@ void WaterObject::DrawPreview(rct_drawpixelinfo * dpi, sint32 width, sint32 heig
sint32 y = height / 2;
gfx_draw_string_centred(dpi, STR_WINDOW_NO_IMAGE, x, y, COLOUR_BLACK, nullptr);
}
void WaterObject::ReadJson(IReadObjectContext * context, const json_t * root)
{
auto properties = json_object_get(root, "properties");
_legacyType.flags = ObjectJsonHelpers::GetFlags<uint16>(properties, {
{ "allowDucks", WATER_FLAGS_ALLOW_DUCKS }});
ObjectJsonHelpers::LoadStrings(root, GetStringTable());
// Images which are actually palette data
static const char * paletteNames[] =
{
"general",
"waves-0",
"waves-1",
"waves-2",
"sparkles-0",
"sparkles-1",
"sparkles-2"
};
for (auto paletteName : paletteNames)
{
auto jPalettes = json_object_get(properties, "palettes");
if (jPalettes != nullptr)
{
auto jPalette = json_object_get(jPalettes, paletteName);
if (jPalette != nullptr)
{
ReadJsonPalette(jPalette);
}
}
}
}
void WaterObject::ReadJsonPalette(const json_t * jPalette)
{
auto paletteStartIndex = json_integer_value(json_object_get(jPalette, "index"));
auto jColours = json_object_get(jPalette, "colours");
auto numColours = json_array_size(jColours);
auto data = std::make_unique<uint8[]>(numColours * 3);
size_t dataIndex = 0;
size_t index;
const json_t * jColour;
json_array_foreach(jColours, index, jColour)
{
auto szColour = json_string_value(jColour);
if (szColour != nullptr)
{
auto colour = ParseColour(szColour);
data[dataIndex + 0] = (colour >> 16) & 0xFF;
data[dataIndex + 1] = (colour >> 8) & 0xFF;
data[dataIndex + 2] = colour & 0xFF;
}
dataIndex += 3;
}
rct_g1_element g1 = { 0 };
g1.offset = data.get();
g1.width = (sint16)numColours;
g1.x_offset = (sint16)paletteStartIndex;
g1.flags = G1_FLAG_PALETTE;
auto &imageTable = GetImageTable();
imageTable.AddImage(&g1);
}
uint32 WaterObject::ParseColour(const std::string &s) const
{
uint8 r = 0;
uint8 g = 0;
uint8 b = 0;
if (s[0] == '#' && s.size() == 7)
{
// Expect #RRGGBB
r = std::stoul(s.substr(1, 2), nullptr, 16) & 0xFF;
g = std::stoul(s.substr(3, 2), nullptr, 16) & 0xFF;
b = std::stoul(s.substr(5, 2), nullptr, 16) & 0xFF;
}
return (b << 16) | (g << 8) | r;
}

View File

@ -16,9 +16,9 @@
#pragma once
#include "Object.h"
#include <tuple>
#include "../world/Water.h"
#include "Object.h"
class WaterObject final : public Object
{
@ -30,9 +30,14 @@ public:
void * GetLegacyData() override { return &_legacyType; }
void ReadJson(IReadObjectContext * context, const json_t * root) override;
void ReadLegacy(IReadObjectContext * context, IStream * stream) override;
void Load() override;
void Unload() override;
void DrawPreview(rct_drawpixelinfo * dpi, sint32 width, sint32 height) const override;
private:
void ReadJsonPalette(const json_t * jPalette);
uint32 ParseColour(const std::string &s) const;
};

View File

@ -39,7 +39,7 @@ static void large_scenery_paint_supports(
uint32 dword_F4387C,
rct_large_scenery_tile * tile)
{
if (tile->var_7 & 0x20) {
if (tile->flags & LARGE_SCENERY_TILE_FLAG_NO_SUPPORTS) {
return;
}
@ -61,7 +61,7 @@ static void large_scenery_paint_supports(
sint32 clearanceHeight = ceil2(tileElement->clearance_height * 8 + 15, 16);
if (tile->var_7 & 0x40) {
if (tile->flags & LARGE_SCENERY_TILE_FLAG_ALLOW_SUPPORTS_ABOVE) {
paint_util_set_segment_support_height(session, SEGMENTS_ALL, clearanceHeight, 0x20);
} else {
paint_util_set_segment_support_height(session, SEGMENTS_ALL, 0xFFFF, 0);
@ -232,7 +232,7 @@ void large_scenery_paint(paint_session * session, uint8 direction, uint16 height
ah = 0x80;
}
ah -= 3;
uint16 edi = tile->var_7;
uint16 edi = tile->flags;
sint32 esi = 16;
if (edi & 0xF00) {
edi &= 0xF000;

View File

@ -850,7 +850,7 @@ static constexpr const uint8 byte_9822F4[] = {
110, // SHOP_ITEM_MEATBALL_SOUP
110, // SHOP_ITEM_FRUIT_JUICE
90, // SHOP_ITEM_SOYBEAN_MILK
100, // SHOP_ITEM_SU_JONGKWA
100, // SHOP_ITEM_SUJEONGGWA
130, // SHOP_ITEM_SUB_SANDWICH
75, // SHOP_ITEM_COOKIE
0, // SHOP_ITEM_EMPTY_BOWL_RED
@ -3021,7 +3021,7 @@ static void peep_update_ride_sub_state_1(rct_peep * peep)
if (ride->type != RIDE_TYPE_ENTERPRISE)
direction_track *= 2;
if (*vehicle_type->peep_loading_positions == 0)
if (vehicle_type->peep_loading_positions[0] == 0)
{
direction_track /= 2;
cl = 0;
@ -3063,7 +3063,7 @@ static void peep_update_ride_sub_state_1(rct_peep * peep)
sint8 load_position = 0;
// Safe, in case current seat > number of loading positions
uint16 numSeatPositions = vehicle_type->peep_loading_positions_count;
auto numSeatPositions = vehicle_type->peep_loading_positions.size();
if (numSeatPositions != 0)
{
size_t loadPositionIndex = numSeatPositions - 1;
@ -3563,7 +3563,7 @@ static void peep_update_ride_sub_state_7(rct_peep * peep)
if (ride->type != RIDE_TYPE_ENTERPRISE)
station_direction *= 2;
if (*vehicle_type->peep_loading_positions == 0)
if (vehicle_type->peep_loading_positions[0] == 0)
{
station_direction /= 2;
cl = 0;

View File

@ -7709,7 +7709,7 @@ bool shop_item_is_food_or_drink(sint32 shopItem)
case SHOP_ITEM_MEATBALL_SOUP:
case SHOP_ITEM_FRUIT_JUICE:
case SHOP_ITEM_SOYBEAN_MILK:
case SHOP_ITEM_SU_JONGKWA:
case SHOP_ITEM_SUJEONGGWA:
case SHOP_ITEM_SUB_SANDWICH:
case SHOP_ITEM_COOKIE:
case SHOP_ITEM_ROAST_SAUSAGE:
@ -7758,7 +7758,7 @@ bool shop_item_is_drink(sint32 shopItem)
case SHOP_ITEM_ICED_TEA:
case SHOP_ITEM_FRUIT_JUICE:
case SHOP_ITEM_SOYBEAN_MILK:
case SHOP_ITEM_SU_JONGKWA:
case SHOP_ITEM_SUJEONGGWA:
return true;
default:
return false;

View File

@ -126,6 +126,7 @@ struct rct_ride_entry {
uint8 shop_item; // 0x1C0
uint8 shop_item_secondary; // 0x1C1
rct_string_id capacity;
void * obj;
};
#pragma pack(pop)
@ -856,7 +857,7 @@ enum {
SHOP_ITEM_MEATBALL_SOUP,
SHOP_ITEM_FRUIT_JUICE,
SHOP_ITEM_SOYBEAN_MILK,
SHOP_ITEM_SU_JONGKWA,
SHOP_ITEM_SUJEONGGWA,
SHOP_ITEM_SUB_SANDWICH,
SHOP_ITEM_COOKIE,
SHOP_ITEM_EMPTY_BOWL_RED,

View File

@ -1460,7 +1460,7 @@ const money8 DefaultShopItemPrice[SHOP_ITEM_COUNT] = {
MONEY(1,50), // SHOP_ITEM_MEATBALL_SOUP
MONEY(1,20), // SHOP_ITEM_FRUIT_JUICE
MONEY(1,20), // SHOP_ITEM_SOYBEAN_MILK
MONEY(1,20), // SHOP_ITEM_SU_JONGKWA
MONEY(1,20), // SHOP_ITEM_SUJEONGGWA
MONEY(1,50), // SHOP_ITEM_SUB_SANDWICH
MONEY(0,70), // SHOP_ITEM_COOKIE
MONEY(0,00), // SHOP_ITEM_EMPTY_BOWL_RED
@ -1576,7 +1576,7 @@ const uint32 ShopItemImage[SHOP_ITEM_COUNT] = {
SPR_SHOP_ITEM_MEATBALL_SOUP,
SPR_SHOP_ITEM_FRUIT_JUICE,
SPR_SHOP_ITEM_SOYBEAN_MILK,
SPR_SHOP_ITEM_SU_JONGKWA,
SPR_SHOP_ITEM_SUJEONGGWA,
SPR_SHOP_ITEM_SUB_SANDWICH,
SPR_SHOP_ITEM_COOKIE,
SPR_SHOP_ITEM_EMPTY_BOWL_RED,
@ -1629,7 +1629,7 @@ const rct_ride_entry_vehicle CableLiftVehicle = {
/* .effect_visual = */ 1,
/* .draw_order = */ 14,
/* .num_vertical_frames_override = */ 0,
/* .peep_loading_positions = */ nullptr
/* .peep_loading_positions = */
};
/* rct2: 0x009A0AA0 */
@ -2482,7 +2482,7 @@ const rct_shop_item_stats ShopItemStats[SHOP_ITEM_COUNT] = {
{ 5, 14, 14, 16 }, // SHOP_ITEM_MEATBALL_SOUP
{ 4, 11, 19, 11 }, // SHOP_ITEM_FRUIT_JUICE
{ 4, 10, 14, 10 }, // SHOP_ITEM_SOYBEAN_MILK
{ 3, 11, 14, 11 }, // SHOP_ITEM_SU_JONGKWA
{ 3, 11, 14, 11 }, // SHOP_ITEM_SUJEONGGWA
{ 5, 19, 19, 17 }, // SHOP_ITEM_SUB_SANDWICH
{ 4, 8, 8, 8 }, // SHOP_ITEM_COOKIE
{ 0, 0, 0, 0 }, // SHOP_ITEM_EMPTY_BOWL_RED

View File

@ -108,7 +108,7 @@ const RideGroup * RideGroupManager::GetRideGroup(const uint8 rideType, const rct
switch (rideType)
{
case RIDE_TYPE_CORKSCREW_ROLLER_COASTER:
if (rideEntry->enabledTrackPieces & (1ULL << TRACK_VERTICAL_LOOP))
if (ride_entry_get_supported_track_pieces(rideEntry) & (1ULL << TRACK_VERTICAL_LOOP))
return &ride_group_corkscrew_rc;
else
return &ride_group_hypercoaster;
@ -118,17 +118,17 @@ const RideGroup * RideGroupManager::GetRideGroup(const uint8 rideType, const rct
else
return &ride_group_junior_rc;
case RIDE_TYPE_CAR_RIDE:
if (rideEntry->enabledTrackPieces & (1ULL << TRACK_SLOPE_STEEP))
if (ride_entry_get_supported_track_pieces(rideEntry) & (1ULL << TRACK_SLOPE_STEEP))
return &ride_group_monster_trucks;
else
return &ride_group_car_ride;
case RIDE_TYPE_TWISTER_ROLLER_COASTER:
if (rideEntry->enabledTrackPieces & (1ULL << TRACK_VERTICAL_LOOP))
if (!(rideEntry->flags & RIDE_ENTRY_FLAG_NO_INVERSIONS))
return &ride_group_steel_twister_rc;
else
return &ride_group_hyper_twister;
case RIDE_TYPE_STEEL_WILD_MOUSE:
if (rideEntry->enabledTrackPieces & (1ULL << TRACK_SLOPE_STEEP))
if (ride_entry_get_supported_track_pieces(rideEntry) & (1ULL << TRACK_SLOPE_STEEP))
return &ride_group_steel_wild_mouse;
else
return &ride_group_spinning_wild_mouse;

View File

@ -1004,7 +1004,6 @@ static money32 track_place(sint32 rideIndex,
direction &= 3;
gTrackGroundFlags = 0;
uint64 enabledTrackPieces = rideEntry->enabledTrackPieces & RideTypePossibleTrackConfigurations[ride->type];
uint32 rideTypeFlags = RideProperties[ride->type].flags;
if ((ride->lifecycle_flags & RIDE_LIFECYCLE_INDESTRUCTIBLE_TRACK) && type == TRACK_ELEM_END_STATION)
@ -1047,7 +1046,7 @@ static money32 track_place(sint32 rideIndex,
}
// Backwards steep lift hills are allowed, even on roller coasters that do not support forwards steep lift hills.
if ((liftHillAndAlternativeState & CONSTRUCTION_LIFT_HILL_SELECTED) &&
!(enabledTrackPieces & (1ULL << TRACK_LIFT_HILL_STEEP)) &&
!(RideTypePossibleTrackConfigurations[ride->type] & (1ULL << TRACK_LIFT_HILL_STEEP)) &&
!gCheatsEnableChainLiftOnAllTrack)
{
if (TrackFlags[type] & TRACK_ELEM_FLAG_IS_STEEP_UP)

View File

@ -17,6 +17,8 @@
#ifndef _VEHICLE_H_
#define _VEHICLE_H_
#include <cstddef>
#include <vector>
#include "../common.h"
#include "../world/Location.hpp"
@ -25,7 +27,9 @@ struct rct_vehicle_colour {
uint8 trim_colour;
};
#ifdef __TESTPAINT__
#pragma pack(push, 1)
#endif // __TESTPAINT__
/**
* Ride type vehicle structure.
* size: 0x65
@ -76,10 +80,20 @@ struct rct_ride_entry_vehicle {
uint8 effect_visual;
uint8 draw_order;
uint8 num_vertical_frames_override; // 0x60 , 0x7A, A custom number that can be used rather than letting RCT2 determine it. Needs the VEHICLE_ENTRY_FLAG_OVERRIDE_NUM_VERTICAL_FRAMES flag to be set.
sint8* peep_loading_positions; // 0x61 , 0x7B
uint16 peep_loading_positions_count;
uint8 pad_61[7]; // 0x61 , 0x7B
std::vector<sint8> peep_loading_positions;
};
#ifdef __TESTPAINT__
#pragma pack(pop)
#endif // __TESTPAINT__
#ifdef PLATFORM_32BIT
static_assert(offsetof(rct_ride_entry_vehicle, peep_loading_positions) % 4 == 0, "Invalid struct layout");
static_assert(sizeof(rct_ride_entry_vehicle) % 4 == 0, "Invalid struct size");
#else
static_assert(offsetof(rct_ride_entry_vehicle, peep_loading_positions) % 8 == 0, "Invalid struct layout");
static_assert(sizeof(rct_ride_entry_vehicle) % 8 == 0, "Invalid struct size");
#endif
struct rct_vehicle {
uint8 sprite_identifier; // 0x00

View File

@ -17,6 +17,8 @@
#ifndef _SPRITES_H_
#define _SPRITES_H_
#include "rct1/RCT1.h"
enum {
SPR_NONE = -1,
@ -99,7 +101,7 @@ enum {
SPR_SHOP_ITEM_MEATBALL_SOUP = 5100,
SPR_SHOP_ITEM_FRUIT_JUICE = 5101,
SPR_SHOP_ITEM_SOYBEAN_MILK = 5102,
SPR_SHOP_ITEM_SU_JONGKWA = 5103,
SPR_SHOP_ITEM_SUJEONGGWA = 5103,
SPR_SHOP_ITEM_SUB_SANDWICH = 5104,
SPR_SHOP_ITEM_COOKIE = 5105,
SPR_SHOP_ITEM_EMPTY_BOWL_RED = 5106,
@ -872,6 +874,13 @@ enum {
SPR_CSG_WALL_TEXTURE_STONE_GREY = SPR_CSG_BEGIN + 47376,
SPR_CSG_WALL_TEXTURE_SKYSCRAPER_A = SPR_CSG_BEGIN + 47377,
SPR_CSG_WALL_TEXTURE_SKYSCRAPER_B = SPR_CSG_BEGIN + 47378,
SPR_CSG_ICE_CREAM_STALL_BEGIN = SPR_CSG_BEGIN + 60625,
SPR_CSG_TOILETS_BEGIN = SPR_CSG_BEGIN + 61289,
SPR_CSG_RIDE_PREVIEWS_BEGIN = SPR_CSG_BEGIN + 64195,
SPR_CSG_RIDE_PREVIEW_ICE_CREAM_STALL = SPR_CSG_RIDE_PREVIEWS_BEGIN + RCT1_RIDE_TYPE_ICE_CREAM_STALL,
SPR_CSG_RIDE_PREVIEW_TOILETS = SPR_CSG_RIDE_PREVIEWS_BEGIN + RCT1_RIDE_TYPE_TOILETS,
};
#endif

View File

@ -2822,7 +2822,7 @@ void game_command_place_large_scenery(sint32* eax, sint32* ebx, sint32* ecx, sin
sint32 zLow = (tile->z_offset + maxHeight) / 8;
sint32 zHigh = (tile->z_clearance / 8) + zLow;
sint32 bx = tile->var_7 >> 12;
sint32 bx = tile->flags >> 12;
bx <<= rotation;
uint8 bl = bx;
uint8 bh = bl >> 4;

View File

@ -56,15 +56,22 @@ struct rct_large_scenery_tile {
sint16 y_offset;
sint16 z_offset;
uint8 z_clearance;
uint16 var_7;
// CCCC WWWW 0SS0 0000
uint16 flags;
};
assert_struct_size(rct_large_scenery_tile, 9);
enum
{
LARGE_SCENERY_TILE_FLAG_NO_SUPPORTS = 0x20,
LARGE_SCENERY_TILE_FLAG_ALLOW_SUPPORTS_ABOVE = 0x40,
};
struct rct_large_scenery_text_glyph {
uint8 image_offset;
uint8 width;
uint8 height;
uint8 var_3;
uint8 pad_3;
};
assert_struct_size(rct_large_scenery_text_glyph, 4);
@ -73,7 +80,7 @@ struct rct_large_scenery_text {
uint16 max_width; // 0x8
uint16 pad_A; // 0xA
uint8 flags; // 0xC
uint8 var_D; // 0xD
uint8 num_images; // 0xD
rct_large_scenery_text_glyph glyphs[256]; // 0xE
};
assert_struct_size(rct_large_scenery_text, 14 + 4 * 256);

View File

@ -222,7 +222,7 @@ static bool WallCheckObstruction(rct_scenery_entry * wall,
tile = &entry->large_scenery.tiles[sequence];
{
sint32 direction = ((edge - tile_element_get_direction(tileElement)) & TILE_ELEMENT_DIRECTION_MASK) + 8;
if (!(tile->var_7 & (1 << direction)))
if (!(tile->flags & (1 << direction)))
{
map_obstruction_set_error_text(tileElement);
return false;