mirror of https://github.com/OpenRCT2/OpenRCT2.git
Merge pull request #10189 from duncanspumpkin/track_ac
Track Design GameAction
This commit is contained in:
commit
b767351a56
|
@ -49,6 +49,7 @@
|
|||
4C93F1AD1F8CD9F000A9330D /* Input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C93F1AC1F8CD9F000A9330D /* Input.cpp */; };
|
||||
4C93F1AF1F8CD9F600A9330D /* KeyboardShortcut.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C93F1AE1F8CD9F600A9330D /* KeyboardShortcut.cpp */; };
|
||||
4CB1375621C2E9F80029FCDA /* SimulateCommands.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CB1375521C2E9F80029FCDA /* SimulateCommands.cpp */; };
|
||||
4CC5258223A19C2900D4366D /* TrackDesignAction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CC5258123A19C2800D4366D /* TrackDesignAction.cpp */; };
|
||||
4CF67197206B7E720034ADDD /* object in Resources */ = {isa = PBXBuildFile; fileRef = 4CF67196206B7E720034ADDD /* object */; };
|
||||
9308D9FE209908090079EE96 /* TileElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9308D9FA209908080079EE96 /* TileElement.cpp */; };
|
||||
9308D9FF209908090079EE96 /* TileElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9308D9FA209908080079EE96 /* TileElement.cpp */; };
|
||||
|
@ -78,8 +79,6 @@
|
|||
9346F9DB208A191900C77D91 /* GuestPathfinding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9346F9D7208A191900C77D91 /* GuestPathfinding.cpp */; };
|
||||
9346F9DC208A191900C77D91 /* GuestPathfinding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9346F9D7208A191900C77D91 /* GuestPathfinding.cpp */; };
|
||||
9346F9DD208A191900C77D91 /* GuestPathfinding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9346F9D7208A191900C77D91 /* GuestPathfinding.cpp */; };
|
||||
937A92132242CCB300B09278 /* LandBuyRightsAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 937A92122242CCB300B09278 /* LandBuyRightsAction.hpp */; };
|
||||
937A92152242CDAA00B09278 /* LandSmoothAction.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 937A92142242CDAA00B09278 /* LandSmoothAction.hpp */; };
|
||||
939A359A20C12FC800630B3F /* Paint.Litter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 939A359720C12FC700630B3F /* Paint.Litter.cpp */; };
|
||||
939A359B20C12FC800630B3F /* Paint.Misc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 939A359820C12FC700630B3F /* Paint.Misc.cpp */; };
|
||||
939A359C20C12FC800630B3F /* Paint.Sprite.h in Headers */ = {isa = PBXBuildFile; fileRef = 939A359920C12FC700630B3F /* Paint.Sprite.h */; };
|
||||
|
@ -634,12 +633,6 @@
|
|||
01C6F0C622FD51FC0057E2F7 /* T6Importer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = T6Importer.cpp; sourceTree = "<group>"; };
|
||||
01C6F0C722FD51FC0057E2F7 /* T6Exporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = T6Exporter.h; sourceTree = "<group>"; };
|
||||
01DDFE6422FD608500221318 /* Window_internal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Window_internal.cpp; sourceTree = "<group>"; };
|
||||
2A43D2B72225B8D900E8F73B /* RideSetVehiclesAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetVehiclesAction.hpp; sourceTree = "<group>"; };
|
||||
2A43D2B82225B8D900E8F73B /* SmallSceneryPlaceAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = SmallSceneryPlaceAction.hpp; sourceTree = "<group>"; };
|
||||
2A43D2B92225B8D900E8F73B /* LoadOrQuitAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LoadOrQuitAction.hpp; sourceTree = "<group>"; };
|
||||
2A43D2BD2225B91A00E8F73B /* RideSetVehiclesAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideSetVehiclesAction.hpp; sourceTree = "<group>"; };
|
||||
2A43D2BE2225B91A00E8F73B /* RideEntranceExitRemoveAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = RideEntranceExitRemoveAction.hpp; sourceTree = "<group>"; };
|
||||
2A43D2BF2225B91A00E8F73B /* LoadOrQuitAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LoadOrQuitAction.hpp; sourceTree = "<group>"; };
|
||||
2A5354E822099C4F00A5440F /* Network.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Network.cpp; sourceTree = "<group>"; };
|
||||
2A5354EA22099C7200A5440F /* CircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CircularBuffer.h; sourceTree = "<group>"; };
|
||||
2ADE2F21224418B1002598AF /* Random.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Random.hpp; sourceTree = "<group>"; };
|
||||
|
@ -889,6 +882,9 @@
|
|||
4CC4B8E91FE00C5D00660D62 /* Input.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Input.h; sourceTree = "<group>"; };
|
||||
4CC4B8EA1FE00C5D00660D62 /* Intro.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Intro.cpp; sourceTree = "<group>"; };
|
||||
4CC4B8EB1FE00C5D00660D62 /* Intro.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Intro.h; sourceTree = "<group>"; };
|
||||
4CC5258023A19C1C00D4366D /* MazePlaceTrackAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = MazePlaceTrackAction.hpp; sourceTree = "<group>"; };
|
||||
4CC5258123A19C2800D4366D /* TrackDesignAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TrackDesignAction.cpp; sourceTree = "<group>"; };
|
||||
4CC5258323A19C2E00D4366D /* TrackDesignAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrackDesignAction.h; sourceTree = "<group>"; };
|
||||
4CDCB0BC20A9902E00321367 /* ShopItem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ShopItem.cpp; sourceTree = "<group>"; };
|
||||
4CDCB0BD20A9902F00321367 /* ShopItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShopItem.h; sourceTree = "<group>"; };
|
||||
4CE4623F1FD0710E0001CD98 /* Game.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Game.cpp; sourceTree = "<group>"; };
|
||||
|
@ -1238,8 +1234,6 @@
|
|||
9350B52820B46E0900897BC5 /* ftrender.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ftrender.h; sourceTree = "<group>"; };
|
||||
9350B52920B46E0900897BC5 /* ft2build.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ft2build.h; sourceTree = "<group>"; };
|
||||
9391535A22D74359008E0780 /* OpenRCT2.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = OpenRCT2.entitlements; sourceTree = "<group>"; };
|
||||
937A92122242CCB300B09278 /* LandBuyRightsAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandBuyRightsAction.hpp; sourceTree = "<group>"; };
|
||||
937A92142242CDAA00B09278 /* LandSmoothAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandSmoothAction.hpp; sourceTree = "<group>"; };
|
||||
939A359720C12FC700630B3F /* Paint.Litter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Paint.Litter.cpp; sourceTree = "<group>"; };
|
||||
939A359820C12FC700630B3F /* Paint.Misc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Paint.Misc.cpp; sourceTree = "<group>"; };
|
||||
939A359920C12FC700630B3F /* Paint.Sprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Paint.Sprite.h; sourceTree = "<group>"; };
|
||||
|
@ -1377,11 +1371,6 @@
|
|||
C6E96E351E0408B40076A04F /* libzip.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libzip.dylib; sourceTree = "<group>"; };
|
||||
C9C630B42235A22C009AD16E /* GameStateSnapshots.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GameStateSnapshots.h; sourceTree = "<group>"; };
|
||||
C9C630B52235A22C009AD16E /* GameStateSnapshots.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GameStateSnapshots.cpp; sourceTree = "<group>"; };
|
||||
C9C630B92235A7E7009AD16E /* LandRaiseAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandRaiseAction.hpp; sourceTree = "<group>"; };
|
||||
C9C630BA2235A7E7009AD16E /* LandLowerAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = LandLowerAction.hpp; sourceTree = "<group>"; };
|
||||
C9C630BB2235A7F9009AD16E /* WaterLowerAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WaterLowerAction.hpp; sourceTree = "<group>"; };
|
||||
C9C630BC2235A7F9009AD16E /* WaterRaiseAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = WaterRaiseAction.hpp; sourceTree = "<group>"; };
|
||||
C9C630BD2235A9C6009AD16E /* FootpathPlaceFromTrackAction.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FootpathPlaceFromTrackAction.hpp; sourceTree = "<group>"; };
|
||||
D41B73EE1C2101890080A7B9 /* libcurl.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libcurl.tbd; path = usr/lib/libcurl.tbd; sourceTree = SDKROOT; };
|
||||
D41B741C1C210A7A0080A7B9 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; };
|
||||
D41B74721C2125E50080A7B9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = distribution/macos/Assets.xcassets; sourceTree = SOURCE_ROOT; };
|
||||
|
@ -2142,6 +2131,7 @@
|
|||
932A211D22D73CFA00C57EDB /* LargeSceneryRemoveAction.hpp */,
|
||||
932A20EC22D73CF200C57EDB /* LargeScenerySetColourAction.hpp */,
|
||||
932A210C22D73CF700C57EDB /* LoadOrQuitAction.hpp */,
|
||||
4CC5258023A19C1C00D4366D /* MazePlaceTrackAction.hpp */,
|
||||
932A20E622D73CF100C57EDB /* MazeSetTrackAction.hpp */,
|
||||
932A211822D73CF900C57EDB /* NetworkModifyGroupAction.hpp */,
|
||||
932A20D822D73CEF00C57EDB /* ParkEntranceRemoveAction.hpp */,
|
||||
|
@ -2185,6 +2175,8 @@
|
|||
932A20FE22D73CF500C57EDB /* StaffSetPatrolAreaAction.hpp */,
|
||||
932A20FA22D73CF400C57EDB /* SurfaceSetStyleAction.hpp */,
|
||||
932A211A22D73CFA00C57EDB /* TileModifyAction.hpp */,
|
||||
4CC5258123A19C2800D4366D /* TrackDesignAction.cpp */,
|
||||
4CC5258323A19C2E00D4366D /* TrackDesignAction.h */,
|
||||
932A210722D73CF600C57EDB /* TrackPlaceAction.hpp */,
|
||||
932A20EF22D73CF200C57EDB /* TrackRemoveAction.hpp */,
|
||||
932A211B22D73CFA00C57EDB /* TrackSetBrakeSpeedAction.hpp */,
|
||||
|
@ -3900,6 +3892,7 @@
|
|||
C685E51C1F8907850090598F /* Map.cpp in Sources */,
|
||||
F7CB864A1EEDA1330030C877 /* KeyboardShortcuts.cpp in Sources */,
|
||||
01C6F0C922FD51FC0057E2F7 /* T6Importer.cpp in Sources */,
|
||||
4CC5258223A19C2900D4366D /* TrackDesignAction.cpp in Sources */,
|
||||
C654DF381F69C0430040F43D /* StaffFirePrompt.cpp in Sources */,
|
||||
C68313D51FDB4F4C006DB3D8 /* Graph.cpp in Sources */,
|
||||
C685E51D1F8907850090598F /* Research.cpp in Sources */,
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
------------------------------------------------------------------------
|
||||
- Feature: [#7865] Transport rides can now be synchronised.
|
||||
- Feature: [#10305] Add two shortcuts for increasing and decreasing the scaling factor.
|
||||
- Feature: [#10189] Make Track Designs work in multiplayer.
|
||||
- Change: [#1164] Use available translations for shortcut key bindings.
|
||||
- Fix: [#10228] Can't import RCT1 Deluxe from Steam.
|
||||
- Fix: [#10325] Crash when banners have no text.
|
||||
|
|
|
@ -440,6 +440,14 @@ public:
|
|||
window_invalidate_by_class(WC_FINANCES);
|
||||
window_invalidate_by_class(WC_RESEARCH);
|
||||
break;
|
||||
|
||||
case INTENT_ACTION_TRACK_DESIGN_REMOVE_PROVISIONAL:
|
||||
TrackPlaceClearProvisionalTemporarily();
|
||||
break;
|
||||
|
||||
case INTENT_ACTION_TRACK_DESIGN_RESTORE_PROVISIONAL:
|
||||
TrackPlaceRestoreProvisional();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -981,12 +981,7 @@ static void window_new_ride_paint_ride_information(
|
|||
gfx_draw_string_left_clipped(dpi, STR_AVAILABLE_VEHICLES, &drawString, COLOUR_BLACK, x, y + 39, WW - 2);
|
||||
}
|
||||
|
||||
// Track designs are disabled in multiplayer, so don't say there are any designs available when in multiplayer
|
||||
if (network_get_mode() != NETWORK_MODE_NONE)
|
||||
{
|
||||
_lastTrackDesignCount = 0;
|
||||
}
|
||||
else if (item.type != _lastTrackDesignCountRideType.type || item.entry_index != _lastTrackDesignCountRideType.entry_index)
|
||||
if (item.type != _lastTrackDesignCountRideType.type || item.entry_index != _lastTrackDesignCountRideType.entry_index)
|
||||
{
|
||||
_lastTrackDesignCountRideType = item;
|
||||
_lastTrackDesignCount = get_num_track_designs(item);
|
||||
|
@ -1047,17 +1042,7 @@ static void window_new_ride_select(rct_window* w)
|
|||
window_close(w);
|
||||
window_close_construction_windows();
|
||||
|
||||
bool allowTrackDesigns = true;
|
||||
#ifndef NETWORK_DISABLE
|
||||
// TODO: FIX NETWORK TRACKS
|
||||
// Until tracks work with the network this will disable them
|
||||
if (network_get_mode() != NETWORK_MODE_NONE)
|
||||
{
|
||||
allowTrackDesigns = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (allowTrackDesigns && _lastTrackDesignCount > 0)
|
||||
if (_lastTrackDesignCount > 0)
|
||||
{
|
||||
auto intent = Intent(WC_TRACK_DESIGN_LIST);
|
||||
intent.putExtra(INTENT_EXTRA_RIDE_TYPE, item.type);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <openrct2/Context.h>
|
||||
#include <openrct2/Game.h>
|
||||
#include <openrct2/Input.h>
|
||||
#include <openrct2/actions/TrackDesignAction.h>
|
||||
#include <openrct2/audio/audio.h>
|
||||
#include <openrct2/localisation/Localisation.h>
|
||||
#include <openrct2/ride/Track.h>
|
||||
|
@ -119,8 +120,6 @@ static std::unique_ptr<TrackDesign> _trackDesign;
|
|||
|
||||
static void window_track_place_clear_provisional();
|
||||
static int32_t window_track_place_get_base_z(int32_t x, int32_t y);
|
||||
static void window_track_place_attempt_placement(
|
||||
TrackDesign* td6, int32_t x, int32_t y, int32_t z, int32_t bl, money32* cost, ride_id_t* rideIndex);
|
||||
|
||||
static void window_track_place_clear_mini_preview();
|
||||
static void window_track_place_draw_mini_preview(TrackDesign* td6);
|
||||
|
@ -240,6 +239,25 @@ static void window_track_place_update(rct_window* w)
|
|||
window_close(w);
|
||||
}
|
||||
|
||||
static GameActionResult::Ptr FindValidTrackDesignPlaceHeight(CoordsXYZ& loc, uint32_t flags)
|
||||
{
|
||||
GameActionResult::Ptr res;
|
||||
for (int32_t i = 0; i < 7; i++, loc.z += 8)
|
||||
{
|
||||
auto tdAction = TrackDesignAction(CoordsXYZD{ loc.x, loc.y, loc.z, _currentTrackPieceDirection }, *_trackDesign);
|
||||
tdAction.SetFlags(flags);
|
||||
res = GameActions::Query(&tdAction);
|
||||
|
||||
// If successful dont keep trying.
|
||||
// If failure due to no money then increasing height only makes problem worse
|
||||
if (res->Error == GA_ERROR::OK || res->Error == GA_ERROR::INSUFFICIENT_FUNDS)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006CFF2D
|
||||
|
@ -273,31 +291,35 @@ static void window_track_place_toolupdate(rct_window* w, rct_widgetindex widgetI
|
|||
|
||||
// Get base Z position
|
||||
mapZ = window_track_place_get_base_z(mapCoords.x, mapCoords.y);
|
||||
CoordsXYZ trackLoc = { mapCoords, mapZ };
|
||||
|
||||
if (game_is_not_paused() || gCheatsBuildInPauseMode)
|
||||
{
|
||||
window_track_place_clear_provisional();
|
||||
auto res = FindValidTrackDesignPlaceHeight(trackLoc, GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST);
|
||||
|
||||
// Try increasing Z until a feasible placement is found
|
||||
for (int32_t i = 0; i < 7; i++)
|
||||
if (res->Error == GA_ERROR::OK)
|
||||
{
|
||||
ride_id_t rideIndex;
|
||||
uint16_t flags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST;
|
||||
window_track_place_attempt_placement(_trackDesign.get(), mapCoords.x, mapCoords.y, mapZ, flags, &cost, &rideIndex);
|
||||
if (cost != MONEY32_UNDEFINED)
|
||||
{
|
||||
_window_track_place_ride_index = rideIndex;
|
||||
_window_track_place_last_valid_x = mapCoords.x;
|
||||
_window_track_place_last_valid_y = mapCoords.y;
|
||||
_window_track_place_last_valid_z = mapZ;
|
||||
_window_track_place_last_was_valid = true;
|
||||
break;
|
||||
}
|
||||
mapZ += 8;
|
||||
// Valid location found. Place the ghost at the location.
|
||||
auto tdAction = TrackDesignAction({ trackLoc, _currentTrackPieceDirection }, *_trackDesign);
|
||||
tdAction.SetFlags(GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST);
|
||||
tdAction.SetCallback([trackLoc](const GameAction*, const TrackDesignActionResult* result) {
|
||||
if (result->Error == GA_ERROR::OK)
|
||||
{
|
||||
_window_track_place_ride_index = result->rideIndex;
|
||||
_window_track_place_last_valid_x = trackLoc.x;
|
||||
_window_track_place_last_valid_y = trackLoc.y;
|
||||
_window_track_place_last_valid_z = trackLoc.z;
|
||||
_window_track_place_last_was_valid = true;
|
||||
}
|
||||
});
|
||||
res = GameActions::Execute(&tdAction);
|
||||
cost = res->Error == GA_ERROR::OK ? res->Cost : MONEY32_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
_window_track_place_last_x = mapCoords.x;
|
||||
_window_track_place_last_y = mapCoords.y;
|
||||
_window_track_place_last_x = trackLoc.x;
|
||||
_window_track_place_last_y = trackLoc.y;
|
||||
if (cost != _window_track_place_last_cost)
|
||||
{
|
||||
_window_track_place_last_cost = cost;
|
||||
|
@ -305,7 +327,7 @@ static void window_track_place_toolupdate(rct_window* w, rct_widgetindex widgetI
|
|||
}
|
||||
|
||||
place_virtual_track(
|
||||
_trackDesign.get(), PTD_OPERATION_DRAW_OUTLINES, true, GetOrAllocateRide(0), mapCoords.x, mapCoords.y, mapZ);
|
||||
_trackDesign.get(), PTD_OPERATION_DRAW_OUTLINES, true, GetOrAllocateRide(0), trackLoc.x, trackLoc.y, trackLoc.z);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -326,49 +348,50 @@ static void window_track_place_tooldown(rct_window* w, rct_widgetindex widgetInd
|
|||
|
||||
// Try increasing Z until a feasible placement is found
|
||||
int16_t mapZ = window_track_place_get_base_z(mapCoords.x, mapCoords.y);
|
||||
for (int32_t i = 0; i < 7; i++)
|
||||
CoordsXYZ trackLoc = { mapCoords, mapZ };
|
||||
|
||||
auto res = FindValidTrackDesignPlaceHeight(trackLoc, 0);
|
||||
if (res->Error == GA_ERROR::OK)
|
||||
{
|
||||
gDisableErrorWindowSound = true;
|
||||
money32 cost = MONEY32_UNDEFINED;
|
||||
ride_id_t rideIndex = RIDE_ID_NULL;
|
||||
window_track_place_attempt_placement(_trackDesign.get(), mapCoords.x, mapCoords.y, mapZ, 1, &cost, &rideIndex);
|
||||
gDisableErrorWindowSound = false;
|
||||
|
||||
if (cost != MONEY32_UNDEFINED)
|
||||
{
|
||||
auto ride = get_ride(rideIndex);
|
||||
if (ride != nullptr)
|
||||
auto tdAction = TrackDesignAction({ trackLoc, _currentTrackPieceDirection }, *_trackDesign);
|
||||
tdAction.SetCallback([trackLoc](const GameAction*, const TrackDesignActionResult* result) {
|
||||
if (result->Error == GA_ERROR::OK)
|
||||
{
|
||||
window_close_by_class(WC_ERROR);
|
||||
audio_play_sound_at_location(SoundId::PlaceItem, { mapCoords.x, mapCoords.y, mapZ });
|
||||
auto ride = get_ride(result->rideIndex);
|
||||
if (ride != nullptr)
|
||||
{
|
||||
window_close_by_class(WC_ERROR);
|
||||
audio_play_sound_at_location(SoundId::PlaceItem, trackLoc);
|
||||
|
||||
_currentRideIndex = rideIndex;
|
||||
if (track_design_are_entrance_and_exit_placed())
|
||||
{
|
||||
auto intent = Intent(WC_RIDE);
|
||||
intent.putExtra(INTENT_EXTRA_RIDE_ID, rideIndex);
|
||||
context_open_intent(&intent);
|
||||
window_close(w);
|
||||
}
|
||||
else
|
||||
{
|
||||
ride_initialise_construction_window(ride);
|
||||
w = window_find_by_class(WC_RIDE_CONSTRUCTION);
|
||||
window_event_mouse_up_call(w, WC_RIDE_CONSTRUCTION__WIDX_ENTRANCE);
|
||||
_currentRideIndex = result->rideIndex;
|
||||
if (track_design_are_entrance_and_exit_placed())
|
||||
{
|
||||
auto intent = Intent(WC_RIDE);
|
||||
intent.putExtra(INTENT_EXTRA_RIDE_ID, result->rideIndex);
|
||||
context_open_intent(&intent);
|
||||
auto wnd = window_find_by_class(WC_TRACK_DESIGN_PLACE);
|
||||
window_close(wnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
ride_initialise_construction_window(ride);
|
||||
auto wnd = window_find_by_class(WC_RIDE_CONSTRUCTION);
|
||||
window_event_mouse_up_call(wnd, WC_RIDE_CONSTRUCTION__WIDX_ENTRANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if player did not have enough funds
|
||||
if (gGameCommandErrorText == STR_NOT_ENOUGH_CASH_REQUIRES)
|
||||
break;
|
||||
|
||||
mapZ += 8;
|
||||
else
|
||||
{
|
||||
audio_play_sound_at_location(SoundId::Error, result->Position);
|
||||
}
|
||||
});
|
||||
GameActions::Execute(&tdAction);
|
||||
return;
|
||||
}
|
||||
|
||||
// Unable to build track
|
||||
audio_play_sound_at_location(SoundId::Error, { mapCoords.x, mapCoords.y, mapZ });
|
||||
audio_play_sound_at_location(SoundId::Error, trackLoc);
|
||||
context_show_error(res->ErrorTitle, res->ErrorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -413,6 +436,37 @@ static void window_track_place_clear_provisional()
|
|||
}
|
||||
}
|
||||
|
||||
void TrackPlaceClearProvisionalTemporarily()
|
||||
{
|
||||
if (_window_track_place_last_was_valid)
|
||||
{
|
||||
auto ride = get_ride(_window_track_place_ride_index);
|
||||
if (ride != nullptr)
|
||||
{
|
||||
place_virtual_track(
|
||||
_trackDesign.get(), PTD_OPERATION_REMOVE_GHOST, true, ride, _window_track_place_last_valid_x,
|
||||
_window_track_place_last_valid_y, _window_track_place_last_valid_z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TrackPlaceRestoreProvisional()
|
||||
{
|
||||
if (_window_track_place_last_was_valid)
|
||||
{
|
||||
auto tdAction = TrackDesignAction(
|
||||
{ _window_track_place_last_valid_x, _window_track_place_last_valid_y, _window_track_place_last_valid_z,
|
||||
_currentTrackPieceDirection },
|
||||
*_trackDesign);
|
||||
tdAction.SetFlags(GAME_COMMAND_FLAG_NO_SPEND | GAME_COMMAND_FLAG_GHOST);
|
||||
auto res = GameActions::Execute(&tdAction);
|
||||
if (res->Error != GA_ERROR::OK)
|
||||
{
|
||||
_window_track_place_last_was_valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006D17C6
|
||||
|
@ -443,28 +497,6 @@ static int32_t window_track_place_get_base_z(int32_t x, int32_t y)
|
|||
return z + place_virtual_track(_trackDesign.get(), PTD_OPERATION_GET_PLACE_Z, true, GetOrAllocateRide(0), x, y, z);
|
||||
}
|
||||
|
||||
static void window_track_place_attempt_placement(
|
||||
TrackDesign* td6, int32_t x, int32_t y, int32_t z, int32_t bl, money32* cost, ride_id_t* rideIndex)
|
||||
{
|
||||
int32_t eax, ebx, ecx, edx, esi, edi, ebp;
|
||||
money32 result;
|
||||
|
||||
edx = esi = ebp = 0;
|
||||
eax = x;
|
||||
ebx = bl;
|
||||
ecx = y;
|
||||
edi = z;
|
||||
|
||||
gActiveTrackDesign = _trackDesign.get();
|
||||
result = game_do_command_p(GAME_COMMAND_PLACE_TRACK_DESIGN, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp);
|
||||
gActiveTrackDesign = nullptr;
|
||||
|
||||
if (cost != nullptr)
|
||||
*cost = result;
|
||||
if (rideIndex != nullptr)
|
||||
*rideIndex = edi & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006CFD9D
|
||||
|
|
|
@ -95,6 +95,9 @@ rct_window* window_loadsave_open(int32_t type, const char* defaultName, loadsave
|
|||
rct_window* window_track_place_open(const struct track_design_file_ref* tdFileRef);
|
||||
rct_window* window_track_manage_open(struct track_design_file_ref* tdFileRef);
|
||||
|
||||
void TrackPlaceClearProvisionalTemporarily();
|
||||
void TrackPlaceRestoreProvisional();
|
||||
|
||||
rct_window* window_map_open();
|
||||
void window_map_reset();
|
||||
|
||||
|
|
|
@ -73,7 +73,6 @@ bool gDoSingleUpdate = false;
|
|||
float gDayNightCycle = 0;
|
||||
bool gInUpdateCode = false;
|
||||
bool gInMapInitCode = false;
|
||||
int32_t gGameCommandNestLevel;
|
||||
std::string gCurrentLoadedPath;
|
||||
|
||||
bool gLoadKeepWindowsOpen = false;
|
||||
|
@ -284,160 +283,6 @@ void update_palette_effects()
|
|||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* rct2: 0x0069C62C
|
||||
*
|
||||
* @param cost (ebp)
|
||||
*/
|
||||
static int32_t game_check_affordability(int32_t cost, uint32_t flags)
|
||||
{
|
||||
if (finance_check_affordability(cost, flags))
|
||||
return cost;
|
||||
|
||||
set_format_arg(0, uint32_t, cost);
|
||||
gGameCommandErrorText = STR_NOT_ENOUGH_CASH_REQUIRES;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006677F2
|
||||
*
|
||||
* @param ebx flags
|
||||
* @param esi command
|
||||
*/
|
||||
int32_t game_do_command(int32_t eax, int32_t ebx, int32_t ecx, int32_t edx, int32_t esi, int32_t edi, int32_t ebp)
|
||||
{
|
||||
return game_do_command_p(esi, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006677F2 with pointers as arguments
|
||||
*
|
||||
* @param ebx flags
|
||||
* @param esi command
|
||||
*/
|
||||
int32_t game_do_command_p(
|
||||
uint32_t command, int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp)
|
||||
{
|
||||
int32_t cost, flags;
|
||||
int32_t original_ebx, original_edx, original_esi, original_edi, original_ebp;
|
||||
|
||||
*esi = command;
|
||||
original_ebx = *ebx;
|
||||
original_edx = *edx;
|
||||
original_esi = *esi;
|
||||
original_edi = *edi;
|
||||
original_ebp = *ebp;
|
||||
|
||||
if (command >= std::size(new_game_command_table))
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
flags = *ebx;
|
||||
|
||||
auto* replayManager = GetContext()->GetReplayManager();
|
||||
if (replayManager->IsReplaying())
|
||||
{
|
||||
// We only accept replay commands as long the replay is active.
|
||||
if ((flags & GAME_COMMAND_FLAG_REPLAY) == 0)
|
||||
{
|
||||
// TODO: Introduce proper error.
|
||||
gGameCommandErrorText = STR_CHEAT_BUILD_IN_PAUSE_MODE;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
if (gGameCommandNestLevel == 0)
|
||||
{
|
||||
gGameCommandErrorText = STR_NONE;
|
||||
}
|
||||
|
||||
// Increment nest count
|
||||
gGameCommandNestLevel++;
|
||||
|
||||
*ebx &= ~GAME_COMMAND_FLAG_APPLY;
|
||||
|
||||
// Make sure the camera position won't change if the command skips setting them.
|
||||
gCommandPosition.x = LOCATION_NULL;
|
||||
gCommandPosition.y = LOCATION_NULL;
|
||||
gCommandPosition.z = LOCATION_NULL;
|
||||
|
||||
// First call for validity and price check
|
||||
new_game_command_table[command](eax, ebx, ecx, edx, esi, edi, ebp);
|
||||
cost = *ebx;
|
||||
|
||||
if (cost != MONEY32_UNDEFINED)
|
||||
{
|
||||
// Check funds
|
||||
int32_t insufficientFunds = 0;
|
||||
if (gGameCommandNestLevel == 1 && !(flags & GAME_COMMAND_FLAG_NO_SPEND) && cost != 0)
|
||||
insufficientFunds = game_check_affordability(cost, flags);
|
||||
|
||||
if (insufficientFunds != MONEY32_UNDEFINED)
|
||||
{
|
||||
*ebx = original_ebx;
|
||||
*edx = original_edx;
|
||||
*esi = original_esi;
|
||||
*edi = original_edi;
|
||||
*ebp = original_ebp;
|
||||
|
||||
if (!(flags & GAME_COMMAND_FLAG_APPLY))
|
||||
{
|
||||
// Decrement nest count
|
||||
gGameCommandNestLevel--;
|
||||
return cost;
|
||||
}
|
||||
|
||||
// Second call to actually perform the operation
|
||||
new_game_command_table[command](eax, ebx, ecx, edx, esi, edi, ebp);
|
||||
|
||||
*edx = *ebx;
|
||||
|
||||
if (*edx != MONEY32_UNDEFINED && *edx < cost)
|
||||
cost = *edx;
|
||||
|
||||
// Decrement nest count
|
||||
gGameCommandNestLevel--;
|
||||
if (gGameCommandNestLevel != 0)
|
||||
return cost;
|
||||
|
||||
// Check if money is required.
|
||||
if (finance_check_money_required(flags))
|
||||
{
|
||||
// Update money balance
|
||||
finance_payment(cost, gCommandExpenditureType);
|
||||
|
||||
// Create a +/- money text effect
|
||||
if (cost != 0 && game_is_not_paused())
|
||||
rct_money_effect::Create(cost);
|
||||
}
|
||||
|
||||
// Start autosave timer after game command
|
||||
if (gLastAutoSaveUpdate == AUTOSAVE_PAUSE)
|
||||
gLastAutoSaveUpdate = platform_get_ticks();
|
||||
|
||||
return cost;
|
||||
}
|
||||
}
|
||||
|
||||
// Error occurred
|
||||
|
||||
// Decrement nest count
|
||||
gGameCommandNestLevel--;
|
||||
|
||||
// Show error window
|
||||
if (gGameCommandNestLevel == 0 && (flags & GAME_COMMAND_FLAG_APPLY) && !(flags & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED)
|
||||
&& !(flags & GAME_COMMAND_FLAG_NETWORKED) && !(flags & GAME_COMMAND_FLAG_GHOST))
|
||||
{
|
||||
context_show_error(gGameCommandErrorTitle, gGameCommandErrorText);
|
||||
}
|
||||
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
void pause_toggle()
|
||||
{
|
||||
|
@ -996,17 +841,3 @@ void game_load_or_quit_no_save_prompt()
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT] = {
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, game_command_place_track_design,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, NULL,
|
||||
};
|
||||
|
|
|
@ -66,38 +66,38 @@ enum GAME_COMMAND
|
|||
GAME_COMMAND_REMOVE_LARGE_SCENERY, // GA
|
||||
GAME_COMMAND_SET_CURRENT_LOAN, // GA
|
||||
GAME_COMMAND_SET_RESEARCH_FUNDING, // GA
|
||||
GAME_COMMAND_PLACE_TRACK_DESIGN,
|
||||
GAME_COMMAND_START_MARKETING_CAMPAIGN, // GA
|
||||
GAME_COMMAND_PLACE_MAZE_DESIGN, // GA
|
||||
GAME_COMMAND_PLACE_BANNER, // GA
|
||||
GAME_COMMAND_REMOVE_BANNER, // GA
|
||||
GAME_COMMAND_SET_SCENERY_COLOUR, // GA
|
||||
GAME_COMMAND_SET_WALL_COLOUR, // GA
|
||||
GAME_COMMAND_SET_LARGE_SCENERY_COLOUR, // GA
|
||||
GAME_COMMAND_SET_BANNER_COLOUR, // GA
|
||||
GAME_COMMAND_SET_LAND_OWNERSHIP, // GA
|
||||
GAME_COMMAND_CLEAR_SCENERY, // GA
|
||||
GAME_COMMAND_SET_BANNER_NAME, // GA
|
||||
GAME_COMMAND_SET_SIGN_NAME, // GA
|
||||
GAME_COMMAND_SET_BANNER_STYLE, // GA
|
||||
GAME_COMMAND_SET_SIGN_STYLE, // GA
|
||||
GAME_COMMAND_SET_PLAYER_GROUP, // GA
|
||||
GAME_COMMAND_MODIFY_GROUPS, // GA
|
||||
GAME_COMMAND_KICK_PLAYER, // GA
|
||||
GAME_COMMAND_CHEAT, // GA
|
||||
GAME_COMMAND_PICKUP_GUEST, // GA
|
||||
GAME_COMMAND_PICKUP_STAFF, // GA
|
||||
GAME_COMMAND_BALLOON_PRESS, // GA
|
||||
GAME_COMMAND_MODIFY_TILE, // GA
|
||||
GAME_COMMAND_EDIT_SCENARIO_OPTIONS, // GA
|
||||
GAME_COMMAND_PLACE_PEEP_SPAWN, // GA, TODO: refactor to separate array for just game actions
|
||||
GAME_COMMAND_SET_CLIMATE, // GA
|
||||
GAME_COMMAND_SET_COLOUR_SCHEME, // GA
|
||||
GAME_COMMAND_SET_STAFF_COSTUME, // GA
|
||||
GAME_COMMAND_PLACE_FOOTPATH_SCENERY, // GA
|
||||
GAME_COMMAND_REMOVE_FOOTPATH_SCENERY, // GA
|
||||
GAME_COMMAND_GUEST_SET_FLAGS, // GA
|
||||
GAME_COMMAND_SET_DATE, // GA
|
||||
GAME_COMMAND_PLACE_TRACK_DESIGN, // GA
|
||||
GAME_COMMAND_START_MARKETING_CAMPAIGN, // GA
|
||||
GAME_COMMAND_PLACE_MAZE_DESIGN, // GA
|
||||
GAME_COMMAND_PLACE_BANNER, // GA
|
||||
GAME_COMMAND_REMOVE_BANNER, // GA
|
||||
GAME_COMMAND_SET_SCENERY_COLOUR, // GA
|
||||
GAME_COMMAND_SET_WALL_COLOUR, // GA
|
||||
GAME_COMMAND_SET_LARGE_SCENERY_COLOUR, // GA
|
||||
GAME_COMMAND_SET_BANNER_COLOUR, // GA
|
||||
GAME_COMMAND_SET_LAND_OWNERSHIP, // GA
|
||||
GAME_COMMAND_CLEAR_SCENERY, // GA
|
||||
GAME_COMMAND_SET_BANNER_NAME, // GA
|
||||
GAME_COMMAND_SET_SIGN_NAME, // GA
|
||||
GAME_COMMAND_SET_BANNER_STYLE, // GA
|
||||
GAME_COMMAND_SET_SIGN_STYLE, // GA
|
||||
GAME_COMMAND_SET_PLAYER_GROUP, // GA
|
||||
GAME_COMMAND_MODIFY_GROUPS, // GA
|
||||
GAME_COMMAND_KICK_PLAYER, // GA
|
||||
GAME_COMMAND_CHEAT, // GA
|
||||
GAME_COMMAND_PICKUP_GUEST, // GA
|
||||
GAME_COMMAND_PICKUP_STAFF, // GA
|
||||
GAME_COMMAND_BALLOON_PRESS, // GA
|
||||
GAME_COMMAND_MODIFY_TILE, // GA
|
||||
GAME_COMMAND_EDIT_SCENARIO_OPTIONS, // GA
|
||||
GAME_COMMAND_PLACE_PEEP_SPAWN, // GA
|
||||
GAME_COMMAND_SET_CLIMATE, // GA
|
||||
GAME_COMMAND_SET_COLOUR_SCHEME, // GA
|
||||
GAME_COMMAND_SET_STAFF_COSTUME, // GA
|
||||
GAME_COMMAND_PLACE_FOOTPATH_SCENERY, // GA
|
||||
GAME_COMMAND_REMOVE_FOOTPATH_SCENERY, // GA
|
||||
GAME_COMMAND_GUEST_SET_FLAGS, // GA
|
||||
GAME_COMMAND_SET_DATE, // GA
|
||||
GAME_COMMAND_COUNT,
|
||||
};
|
||||
|
||||
|
@ -128,14 +128,9 @@ enum
|
|||
ERROR_TYPE_FILE_LOAD = 255
|
||||
};
|
||||
|
||||
using GAME_COMMAND_POINTER = void(
|
||||
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);
|
||||
|
||||
extern rct_string_id gGameCommandErrorTitle;
|
||||
extern rct_string_id gGameCommandErrorText;
|
||||
|
||||
extern GAME_COMMAND_POINTER* new_game_command_table[GAME_COMMAND_COUNT];
|
||||
|
||||
extern uint32_t gCurrentTicks;
|
||||
extern uint32_t gCurrentRealTimeTicks;
|
||||
|
||||
|
@ -146,7 +141,6 @@ extern bool gDoSingleUpdate;
|
|||
extern float gDayNightCycle;
|
||||
extern bool gInUpdateCode;
|
||||
extern bool gInMapInitCode;
|
||||
extern int32_t gGameCommandNestLevel;
|
||||
extern std::string gCurrentLoadedPath;
|
||||
|
||||
extern bool gLoadKeepWindowsOpen;
|
||||
|
@ -158,10 +152,6 @@ void game_create_windows();
|
|||
void reset_all_sprite_quadrant_placements();
|
||||
void update_palette_effects();
|
||||
|
||||
int32_t game_do_command(int32_t eax, int32_t ebx, int32_t ecx, int32_t edx, int32_t esi, int32_t edi, int32_t ebp);
|
||||
int32_t game_do_command_p(
|
||||
uint32_t command, int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);
|
||||
|
||||
void game_load_or_quit_no_save_prompt();
|
||||
void load_from_sv6(const char* path);
|
||||
void game_load_init();
|
||||
|
|
|
@ -218,7 +218,6 @@ void GameState::Update()
|
|||
pause_toggle();
|
||||
}
|
||||
|
||||
gGameCommandNestLevel = 0;
|
||||
gDoSingleUpdate = false;
|
||||
gInUpdateCode = false;
|
||||
}
|
||||
|
|
|
@ -91,30 +91,6 @@ void ride_construct_new(ride_list_item listItem)
|
|||
GameActions::Execute(&gameAction);
|
||||
}
|
||||
|
||||
money32 ride_create_command(int32_t type, int32_t subType, int32_t flags, ride_id_t* outRideIndex, uint8_t* outRideColour)
|
||||
{
|
||||
int32_t rideEntryIndex = ride_get_entry_index(type, subType);
|
||||
int32_t colour1 = ride_get_random_colour_preset_index(type);
|
||||
int32_t colour2 = ride_get_unused_preset_vehicle_colour(rideEntryIndex);
|
||||
|
||||
auto gameAction = RideCreateAction(type, subType, colour1, colour2);
|
||||
gameAction.SetFlags(flags);
|
||||
|
||||
auto r = GameActions::Execute(&gameAction);
|
||||
const RideCreateGameActionResult* res = static_cast<RideCreateGameActionResult*>(r.get());
|
||||
|
||||
// Callee's of this function expect MONEY32_UNDEFINED in case of failure.
|
||||
if (res->Error != GA_ERROR::OK)
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
*outRideIndex = res->rideIndex;
|
||||
*outRideColour = colour1;
|
||||
|
||||
return res->Cost;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region RideSetStatusAction
|
||||
|
|
|
@ -77,6 +77,7 @@
|
|||
#include "StaffSetPatrolAreaAction.hpp"
|
||||
#include "SurfaceSetStyleAction.hpp"
|
||||
#include "TileModifyAction.hpp"
|
||||
#include "TrackDesignAction.h"
|
||||
#include "TrackPlaceAction.hpp"
|
||||
#include "TrackRemoveAction.hpp"
|
||||
#include "TrackSetBrakeSpeedAction.hpp"
|
||||
|
@ -157,6 +158,7 @@ namespace GameActions
|
|||
Register<LandSetRightsAction>();
|
||||
Register<LandSmoothAction>();
|
||||
Register<TileModifyAction>();
|
||||
Register<TrackDesignAction>();
|
||||
Register<TrackPlaceAction>();
|
||||
Register<TrackRemoveAction>();
|
||||
Register<TrackSetBrakeSpeedAction>();
|
||||
|
|
|
@ -218,6 +218,11 @@ private:
|
|||
|
||||
for (int32_t i = 0; i < PEEP_MAX_THOUGHTS; i++)
|
||||
{
|
||||
// Don't touch items after the first NONE thought as they are not valid
|
||||
// fixes issues with clearing out bad thought data in multiplayer
|
||||
if (peep->thoughts[i].type == PEEP_THOUGHT_TYPE_NONE)
|
||||
break;
|
||||
|
||||
if (peep->thoughts[i].type != PEEP_THOUGHT_TYPE_NONE && peep->thoughts[i].item == _rideIndex)
|
||||
{
|
||||
// Clear top thought, push others up
|
||||
|
|
|
@ -0,0 +1,277 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2019 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "TrackDesignAction.h"
|
||||
|
||||
#include "../management/Finance.h"
|
||||
#include "../management/Research.h"
|
||||
#include "../object/ObjectRepository.h"
|
||||
#include "../ride/RideGroupManager.h"
|
||||
#include "../ride/TrackDesign.h"
|
||||
#include "RideCreateAction.hpp"
|
||||
#include "RideDemolishAction.hpp"
|
||||
#include "RideSetName.hpp"
|
||||
#include "RideSetSetting.hpp"
|
||||
#include "RideSetVehiclesAction.hpp"
|
||||
|
||||
static int32_t place_virtual_track(
|
||||
const TrackDesign& td6, uint8_t ptdOperation, bool placeScenery, Ride* ride, const CoordsXYZ& loc)
|
||||
{
|
||||
return place_virtual_track(const_cast<TrackDesign*>(&td6), ptdOperation, placeScenery, ride, loc.x, loc.y, loc.z);
|
||||
}
|
||||
|
||||
GameActionResult::Ptr TrackDesignAction::Query() const
|
||||
{
|
||||
auto res = MakeResult();
|
||||
res->Position.x = _loc.x + 16;
|
||||
res->Position.y = _loc.y + 16;
|
||||
res->Position.z = _loc.z;
|
||||
res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION;
|
||||
_currentTrackPieceDirection = _loc.direction;
|
||||
|
||||
const rct_object_entry* rideEntryObject = &_td.vehicle_object;
|
||||
|
||||
uint8_t entryType, entryIndex;
|
||||
if (!find_object_in_entry_group(rideEntryObject, &entryType, &entryIndex))
|
||||
{
|
||||
entryIndex = 0xFF;
|
||||
}
|
||||
// Force a fallback if the entry is not invented yet a td6 of it is selected, which can happen in select-by-track-type mode.
|
||||
else if (!ride_entry_is_invented(entryIndex) && !gCheatsIgnoreResearchStatus)
|
||||
{
|
||||
entryIndex = 0xFF;
|
||||
}
|
||||
|
||||
// The rest of the cases are handled by the code in ride_create()
|
||||
if (RideGroupManager::RideTypeHasRideGroups(_td.type) && entryIndex == 0xFF)
|
||||
{
|
||||
const ObjectRepositoryItem* ori = object_repository_find_object_by_name(rideEntryObject->name);
|
||||
if (ori != nullptr)
|
||||
{
|
||||
uint8_t rideGroupIndex = ori->RideInfo.RideGroupIndex;
|
||||
const RideGroup* td6RideGroup = RideGroupManager::RideGroupFind(_td.type, rideGroupIndex);
|
||||
|
||||
uint8_t* availableRideEntries = get_ride_entry_indices_for_ride_type(_td.type);
|
||||
for (uint8_t* rei = availableRideEntries; *rei != RIDE_ENTRY_INDEX_NULL; rei++)
|
||||
{
|
||||
rct_ride_entry* ire = get_ride_entry(*rei);
|
||||
|
||||
if (!ride_entry_is_invented(*rei) && !gCheatsIgnoreResearchStatus)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const RideGroup* irg = RideGroupManager::GetRideGroup(_td.type, ire);
|
||||
if (td6RideGroup->Equals(irg))
|
||||
{
|
||||
entryIndex = *rei;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Colours do not matter as will be overwritten
|
||||
auto rideCreateAction = RideCreateAction(_td.type, entryIndex, 0, 0);
|
||||
rideCreateAction.SetFlags(GetFlags());
|
||||
auto r = GameActions::ExecuteNested(&rideCreateAction);
|
||||
auto rideIndex = static_cast<RideCreateGameActionResult*>(r.get())->rideIndex;
|
||||
|
||||
if (r->Error != GA_ERROR::OK)
|
||||
{
|
||||
return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_CREATE_NEW_RIDE_ATTRACTION, STR_NONE);
|
||||
}
|
||||
|
||||
auto ride = get_ride(rideIndex);
|
||||
if (ride == nullptr)
|
||||
{
|
||||
log_warning("Invalid game command for track placement, ride id = %d", rideIndex);
|
||||
return MakeResult(GA_ERROR::UNKNOWN);
|
||||
}
|
||||
|
||||
money32 cost = 0;
|
||||
|
||||
bool placeScenery = true;
|
||||
cost = place_virtual_track(_td, PTD_OPERATION_PLACE_QUERY, placeScenery, ride, _loc);
|
||||
if (_trackDesignPlaceStateSceneryUnavailable)
|
||||
{
|
||||
placeScenery = false;
|
||||
cost = place_virtual_track(_td, PTD_OPERATION_PLACE_QUERY, placeScenery, ride, _loc);
|
||||
}
|
||||
|
||||
rct_string_id error_reason = gGameCommandErrorText;
|
||||
auto gameAction = RideDemolishAction(ride->id, RIDE_MODIFY_DEMOLISH);
|
||||
gameAction.SetFlags(GetFlags());
|
||||
|
||||
GameActions::ExecuteNested(&gameAction);
|
||||
if (cost == MONEY32_UNDEFINED)
|
||||
{
|
||||
return MakeResult(GA_ERROR::DISALLOWED, error_reason);
|
||||
}
|
||||
res->Cost = cost;
|
||||
return res;
|
||||
}
|
||||
|
||||
GameActionResult::Ptr TrackDesignAction::Execute() const
|
||||
{
|
||||
auto res = MakeResult();
|
||||
res->Position.x = _loc.x + 16;
|
||||
res->Position.y = _loc.y + 16;
|
||||
res->Position.z = _loc.z;
|
||||
res->ExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION;
|
||||
|
||||
const rct_object_entry* rideEntryObject = &_td.vehicle_object;
|
||||
|
||||
uint8_t entryType, entryIndex;
|
||||
if (!find_object_in_entry_group(rideEntryObject, &entryType, &entryIndex))
|
||||
{
|
||||
entryIndex = 0xFF;
|
||||
}
|
||||
// Force a fallback if the entry is not invented yet a td6 of it is selected, which can happen in select-by-track-type mode.
|
||||
else if (!ride_entry_is_invented(entryIndex) && !gCheatsIgnoreResearchStatus)
|
||||
{
|
||||
entryIndex = 0xFF;
|
||||
}
|
||||
|
||||
// The rest of the cases are handled by the code in ride_create()
|
||||
if (RideGroupManager::RideTypeHasRideGroups(_td.type) && entryIndex == 0xFF)
|
||||
{
|
||||
const ObjectRepositoryItem* ori = object_repository_find_object_by_name(rideEntryObject->name);
|
||||
if (ori != nullptr)
|
||||
{
|
||||
uint8_t rideGroupIndex = ori->RideInfo.RideGroupIndex;
|
||||
const RideGroup* td6RideGroup = RideGroupManager::RideGroupFind(_td.type, rideGroupIndex);
|
||||
|
||||
uint8_t* availableRideEntries = get_ride_entry_indices_for_ride_type(_td.type);
|
||||
for (uint8_t* rei = availableRideEntries; *rei != RIDE_ENTRY_INDEX_NULL; rei++)
|
||||
{
|
||||
rct_ride_entry* ire = get_ride_entry(*rei);
|
||||
|
||||
if (!ride_entry_is_invented(*rei) && !gCheatsIgnoreResearchStatus)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const RideGroup* irg = RideGroupManager::GetRideGroup(_td.type, ire);
|
||||
if (td6RideGroup->Equals(irg))
|
||||
{
|
||||
entryIndex = *rei;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Colours do not matter as will be overwritten
|
||||
auto rideCreateAction = RideCreateAction(_td.type, entryIndex, 0, 0);
|
||||
rideCreateAction.SetFlags(GetFlags());
|
||||
auto r = GameActions::ExecuteNested(&rideCreateAction);
|
||||
auto rideIndex = static_cast<RideCreateGameActionResult*>(r.get())->rideIndex;
|
||||
|
||||
if (r->Error != GA_ERROR::OK)
|
||||
{
|
||||
return MakeResult(GA_ERROR::NO_FREE_ELEMENTS, STR_CANT_CREATE_NEW_RIDE_ATTRACTION, STR_NONE);
|
||||
}
|
||||
|
||||
auto ride = get_ride(rideIndex);
|
||||
if (ride == nullptr)
|
||||
{
|
||||
log_warning("Invalid game command for track placement, ride id = %d", rideIndex);
|
||||
return MakeResult(GA_ERROR::UNKNOWN);
|
||||
}
|
||||
|
||||
money32 cost = 0;
|
||||
|
||||
bool placeScenery = true;
|
||||
cost = place_virtual_track(_td, PTD_OPERATION_PLACE_QUERY, placeScenery, ride, _loc);
|
||||
if (_trackDesignPlaceStateSceneryUnavailable)
|
||||
{
|
||||
placeScenery = false;
|
||||
cost = place_virtual_track(_td, PTD_OPERATION_PLACE_QUERY, placeScenery, ride, _loc);
|
||||
}
|
||||
|
||||
if (cost != MONEY32_UNDEFINED)
|
||||
{
|
||||
uint8_t operation;
|
||||
if (GetFlags() & GAME_COMMAND_FLAG_GHOST)
|
||||
{
|
||||
operation = PTD_OPERATION_PLACE_GHOST;
|
||||
}
|
||||
else
|
||||
{
|
||||
operation = PTD_OPERATION_PLACE;
|
||||
}
|
||||
|
||||
cost = place_virtual_track(_td, operation, placeScenery, ride, _loc);
|
||||
}
|
||||
|
||||
if (cost == MONEY32_UNDEFINED)
|
||||
{
|
||||
rct_string_id error_reason = gGameCommandErrorText;
|
||||
auto gameAction = RideDemolishAction(ride->id, RIDE_MODIFY_DEMOLISH);
|
||||
gameAction.SetFlags(GetFlags());
|
||||
|
||||
GameActions::ExecuteNested(&gameAction);
|
||||
return MakeResult(GA_ERROR::DISALLOWED, error_reason);
|
||||
}
|
||||
|
||||
if (entryIndex != 0xFF)
|
||||
{
|
||||
auto colour = ride_get_unused_preset_vehicle_colour(entryIndex);
|
||||
auto rideSetVehicleAction = RideSetVehicleAction(ride->id, RideSetVehicleType::RideEntry, entryIndex, colour);
|
||||
GameActions::ExecuteNested(&rideSetVehicleAction);
|
||||
}
|
||||
|
||||
set_operating_setting_nested(ride->id, RideSetSetting::Mode, _td.ride_mode, GAME_COMMAND_FLAG_APPLY);
|
||||
auto rideSetVehicleAction2 = RideSetVehicleAction(ride->id, RideSetVehicleType::NumTrains, _td.number_of_trains);
|
||||
GameActions::ExecuteNested(&rideSetVehicleAction2);
|
||||
|
||||
auto rideSetVehicleAction3 = RideSetVehicleAction(
|
||||
ride->id, RideSetVehicleType::NumCarsPerTrain, _td.number_of_cars_per_train);
|
||||
GameActions::ExecuteNested(&rideSetVehicleAction3);
|
||||
|
||||
set_operating_setting_nested(ride->id, RideSetSetting::Departure, _td.depart_flags, GAME_COMMAND_FLAG_APPLY);
|
||||
set_operating_setting_nested(ride->id, RideSetSetting::MinWaitingTime, _td.min_waiting_time, GAME_COMMAND_FLAG_APPLY);
|
||||
set_operating_setting_nested(ride->id, RideSetSetting::MaxWaitingTime, _td.max_waiting_time, GAME_COMMAND_FLAG_APPLY);
|
||||
set_operating_setting_nested(ride->id, RideSetSetting::Operation, _td.operation_setting, GAME_COMMAND_FLAG_APPLY);
|
||||
set_operating_setting_nested(ride->id, RideSetSetting::LiftHillSpeed, _td.lift_hill_speed & 0x1F, GAME_COMMAND_FLAG_APPLY);
|
||||
|
||||
uint8_t num_circuits = _td.num_circuits;
|
||||
if (num_circuits == 0)
|
||||
{
|
||||
num_circuits = 1;
|
||||
}
|
||||
set_operating_setting_nested(ride->id, RideSetSetting::NumCircuits, num_circuits, GAME_COMMAND_FLAG_APPLY);
|
||||
ride->SetToDefaultInspectionInterval();
|
||||
ride->lifecycle_flags |= RIDE_LIFECYCLE_NOT_CUSTOM_DESIGN;
|
||||
ride->colour_scheme_type = _td.colour_scheme;
|
||||
|
||||
ride->entrance_style = _td.entrance_style;
|
||||
|
||||
for (int32_t i = 0; i < RCT12_NUM_COLOUR_SCHEMES; i++)
|
||||
{
|
||||
ride->track_colour[i].main = _td.track_spine_colour[i];
|
||||
ride->track_colour[i].additional = _td.track_rail_colour[i];
|
||||
ride->track_colour[i].supports = _td.track_support_colour[i];
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < MAX_VEHICLES_PER_RIDE; i++)
|
||||
{
|
||||
ride->vehicle_colours[i].Body = _td.vehicle_colours[i].body_colour;
|
||||
ride->vehicle_colours[i].Trim = _td.vehicle_colours[i].trim_colour;
|
||||
ride->vehicle_colours[i].Ternary = _td.vehicle_additional_colour[i];
|
||||
}
|
||||
|
||||
auto gameAction = RideSetNameAction(ride->id, _td.name);
|
||||
gameAction.SetFlags(GetFlags());
|
||||
GameActions::ExecuteNested(&gameAction);
|
||||
res->Cost = cost;
|
||||
res->rideIndex = ride->id;
|
||||
return res;
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2019 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../ride/TrackDesign.h"
|
||||
#include "GameAction.h"
|
||||
|
||||
class TrackDesignActionResult final : public GameActionResult
|
||||
{
|
||||
public:
|
||||
TrackDesignActionResult()
|
||||
: GameActionResult(GA_ERROR::OK, STR_NONE)
|
||||
{
|
||||
}
|
||||
TrackDesignActionResult(GA_ERROR error)
|
||||
: GameActionResult(error, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, STR_NONE)
|
||||
{
|
||||
}
|
||||
TrackDesignActionResult(GA_ERROR error, rct_string_id title, rct_string_id message)
|
||||
: GameActionResult(error, title, message)
|
||||
{
|
||||
}
|
||||
TrackDesignActionResult(GA_ERROR error, rct_string_id message)
|
||||
: GameActionResult(error, STR_RIDE_CONSTRUCTION_CANT_CONSTRUCT_THIS_HERE, message)
|
||||
{
|
||||
}
|
||||
|
||||
ride_id_t rideIndex = RIDE_ID_NULL;
|
||||
};
|
||||
|
||||
DEFINE_GAME_ACTION(TrackDesignAction, GAME_COMMAND_PLACE_TRACK_DESIGN, TrackDesignActionResult)
|
||||
{
|
||||
private:
|
||||
CoordsXYZD _loc;
|
||||
TrackDesign _td;
|
||||
|
||||
public:
|
||||
TrackDesignAction() = default;
|
||||
|
||||
TrackDesignAction(CoordsXYZD location, const TrackDesign& td)
|
||||
: _loc(location)
|
||||
, _td(td)
|
||||
{
|
||||
}
|
||||
|
||||
uint16_t GetActionFlags() const override
|
||||
{
|
||||
return GameActionBase::GetActionFlags();
|
||||
}
|
||||
|
||||
void Serialise(DataSerialiser & stream) override
|
||||
{
|
||||
GameAction::Serialise(stream);
|
||||
|
||||
stream << DS_TAG(_loc);
|
||||
_td.Serialise(stream);
|
||||
}
|
||||
|
||||
GameActionResult::Ptr Query() const override;
|
||||
GameActionResult::Ptr Execute() const override;
|
||||
};
|
|
@ -46,6 +46,11 @@ public:
|
|||
return !_isSaving;
|
||||
}
|
||||
|
||||
bool IsLogging() const
|
||||
{
|
||||
return _isLogging;
|
||||
}
|
||||
|
||||
IStream& GetStream()
|
||||
{
|
||||
return _activeStream;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "../network/NetworkTypes.h"
|
||||
#include "../network/network.h"
|
||||
#include "../ride/Ride.h"
|
||||
#include "../ride/TrackDesign.h"
|
||||
#include "../world/Location.hpp"
|
||||
#include "../world/TileElement.h"
|
||||
#include "DataSerialiserTag.h"
|
||||
|
@ -353,6 +354,47 @@ template<typename _Ty, size_t _Size> struct DataSerializerTraits<std::array<_Ty,
|
|||
}
|
||||
};
|
||||
|
||||
template<typename _Ty> struct DataSerializerTraits<std::vector<_Ty>>
|
||||
{
|
||||
static void encode(IStream* stream, const std::vector<_Ty>& val)
|
||||
{
|
||||
uint16_t len = (uint16_t)val.size();
|
||||
uint16_t swapped = ByteSwapBE(len);
|
||||
stream->Write(&swapped);
|
||||
|
||||
DataSerializerTraits<_Ty> s;
|
||||
for (auto&& sub : val)
|
||||
{
|
||||
s.encode(stream, sub);
|
||||
}
|
||||
}
|
||||
static void decode(IStream* stream, std::vector<_Ty>& val)
|
||||
{
|
||||
uint16_t len;
|
||||
stream->Read(&len);
|
||||
len = ByteSwapBE(len);
|
||||
|
||||
DataSerializerTraits<_Ty> s;
|
||||
for (auto i = 0; i < len; ++i)
|
||||
{
|
||||
_Ty sub;
|
||||
s.decode(stream, sub);
|
||||
val.push_back(sub);
|
||||
}
|
||||
}
|
||||
static void log(IStream* stream, const std::vector<_Ty>& val)
|
||||
{
|
||||
stream->Write("{", 1);
|
||||
DataSerializerTraits<_Ty> s;
|
||||
for (auto&& sub : val)
|
||||
{
|
||||
s.log(stream, sub);
|
||||
stream->Write("; ", 2);
|
||||
}
|
||||
stream->Write("}", 1);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct DataSerializerTraits<MapRange>
|
||||
{
|
||||
static void encode(IStream* stream, const MapRange& v)
|
||||
|
@ -516,3 +558,149 @@ template<> struct DataSerializerTraits<NetworkCheatType_t>
|
|||
stream->Write(cheatName, strlen(cheatName));
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct DataSerializerTraits<rct_object_entry>
|
||||
{
|
||||
static void encode(IStream* stream, const rct_object_entry& val)
|
||||
{
|
||||
uint32_t temp = ByteSwapBE(val.flags);
|
||||
stream->Write(&temp);
|
||||
stream->WriteArray(val.nameWOC, 12);
|
||||
}
|
||||
static void decode(IStream* stream, rct_object_entry& val)
|
||||
{
|
||||
uint32_t temp;
|
||||
stream->Read(&temp);
|
||||
val.flags = ByteSwapBE(temp);
|
||||
const char* str = stream->ReadArray<char>(12);
|
||||
memcpy(val.nameWOC, str, 12);
|
||||
}
|
||||
static void log(IStream* stream, const rct_object_entry& val)
|
||||
{
|
||||
stream->WriteArray(val.name, 8);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct DataSerializerTraits<TrackDesignTrackElement>
|
||||
{
|
||||
static void encode(IStream* stream, const TrackDesignTrackElement& val)
|
||||
{
|
||||
stream->Write(&val.flags);
|
||||
stream->Write(&val.type);
|
||||
}
|
||||
static void decode(IStream* stream, TrackDesignTrackElement& val)
|
||||
{
|
||||
stream->Read(&val.flags);
|
||||
stream->Read(&val.type);
|
||||
}
|
||||
static void log(IStream* stream, const TrackDesignTrackElement& val)
|
||||
{
|
||||
char msg[128] = {};
|
||||
snprintf(msg, sizeof(msg), "TrackDesignTrackElement(type = %d, flags = %d)", val.type, val.flags);
|
||||
stream->Write(msg, strlen(msg));
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct DataSerializerTraits<TrackDesignMazeElement>
|
||||
{
|
||||
static void encode(IStream* stream, const TrackDesignMazeElement& val)
|
||||
{
|
||||
uint32_t temp = ByteSwapBE(val.all);
|
||||
stream->Write(&temp);
|
||||
}
|
||||
static void decode(IStream* stream, TrackDesignMazeElement& val)
|
||||
{
|
||||
uint32_t temp;
|
||||
stream->Read(&temp);
|
||||
val.all = ByteSwapBE(temp);
|
||||
}
|
||||
static void log(IStream* stream, const TrackDesignMazeElement& val)
|
||||
{
|
||||
char msg[128] = {};
|
||||
snprintf(msg, sizeof(msg), "TrackDesignMazeElement(all = %d)", val.all);
|
||||
stream->Write(msg, strlen(msg));
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct DataSerializerTraits<TrackDesignEntranceElement>
|
||||
{
|
||||
static void encode(IStream* stream, const TrackDesignEntranceElement& val)
|
||||
{
|
||||
stream->Write(&val.x);
|
||||
stream->Write(&val.y);
|
||||
stream->Write(&val.z);
|
||||
stream->Write(&val.direction);
|
||||
stream->Write(&val.isExit);
|
||||
}
|
||||
static void decode(IStream* stream, TrackDesignEntranceElement& val)
|
||||
{
|
||||
stream->Read(&val.x);
|
||||
stream->Read(&val.y);
|
||||
stream->Read(&val.z);
|
||||
stream->Read(&val.direction);
|
||||
stream->Read(&val.isExit);
|
||||
}
|
||||
static void log(IStream* stream, const TrackDesignEntranceElement& val)
|
||||
{
|
||||
char msg[128] = {};
|
||||
snprintf(
|
||||
msg, sizeof(msg), "TrackDesignEntranceElement(x = %d, y = %d, z = %d, dir = %d, isExit = %d)", val.x, val.y, val.z,
|
||||
val.direction, val.isExit);
|
||||
stream->Write(msg, strlen(msg));
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct DataSerializerTraits<TrackDesignSceneryElement>
|
||||
{
|
||||
static void encode(IStream* stream, const TrackDesignSceneryElement& val)
|
||||
{
|
||||
stream->Write(&val.x);
|
||||
stream->Write(&val.y);
|
||||
stream->Write(&val.z);
|
||||
stream->Write(&val.flags);
|
||||
stream->Write(&val.primary_colour);
|
||||
stream->Write(&val.secondary_colour);
|
||||
DataSerializerTraits<rct_object_entry> s;
|
||||
s.encode(stream, val.scenery_object);
|
||||
}
|
||||
static void decode(IStream* stream, TrackDesignSceneryElement& val)
|
||||
{
|
||||
stream->Read(&val.x);
|
||||
stream->Read(&val.y);
|
||||
stream->Read(&val.z);
|
||||
stream->Read(&val.flags);
|
||||
stream->Read(&val.primary_colour);
|
||||
stream->Read(&val.secondary_colour);
|
||||
DataSerializerTraits<rct_object_entry> s;
|
||||
s.decode(stream, val.scenery_object);
|
||||
}
|
||||
static void log(IStream* stream, const TrackDesignSceneryElement& val)
|
||||
{
|
||||
char msg[128] = {};
|
||||
snprintf(
|
||||
msg, sizeof(msg), "TrackDesignSceneryElement(x = %d, y = %d, z = %d, flags = %d, colour1 = %d, colour2 = %d)",
|
||||
val.x, val.y, val.z, val.flags, val.primary_colour, val.secondary_colour);
|
||||
stream->Write(msg, strlen(msg));
|
||||
stream->WriteArray(val.scenery_object.name, 8);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct DataSerializerTraits<rct_vehicle_colour>
|
||||
{
|
||||
static void encode(IStream* stream, const rct_vehicle_colour& val)
|
||||
{
|
||||
stream->Write(&val.body_colour);
|
||||
stream->Write(&val.trim_colour);
|
||||
}
|
||||
static void decode(IStream* stream, rct_vehicle_colour& val)
|
||||
{
|
||||
stream->Read(&val.body_colour);
|
||||
stream->Read(&val.trim_colour);
|
||||
}
|
||||
static void log(IStream* stream, const rct_vehicle_colour& val)
|
||||
{
|
||||
char msg[128] = {};
|
||||
snprintf(msg, sizeof(msg), "rct_vehicle_colour(body_colour = %d, trim_colour = %d)", val.body_colour, val.trim_colour);
|
||||
stream->Write(msg, strlen(msg));
|
||||
}
|
||||
};
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
// This string specifies which version of network stream current build uses.
|
||||
// It is used for making sure only compatible builds get connected, even within
|
||||
// single OpenRCT2 version.
|
||||
#define NETWORK_STREAM_VERSION "3"
|
||||
#define NETWORK_STREAM_VERSION "4"
|
||||
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION
|
||||
|
||||
static Peep* _pickup_peep = nullptr;
|
||||
|
|
|
@ -53,7 +53,7 @@ bool T6Exporter::SaveTrack(IStream* stream)
|
|||
tempStream.WriteValue<uint32_t>(_trackDesign->flags);
|
||||
tempStream.WriteValue<uint8_t>(_trackDesign->ride_mode);
|
||||
tempStream.WriteValue<uint8_t>((_trackDesign->colour_scheme & 0x3) | (2 << 2));
|
||||
tempStream.WriteArray(_trackDesign->vehicle_colours, RCT12_MAX_VEHICLE_COLOURS);
|
||||
tempStream.WriteArray(_trackDesign->vehicle_colours.data(), RCT12_MAX_VEHICLE_COLOURS);
|
||||
tempStream.WriteValue<uint8_t>(0);
|
||||
tempStream.WriteValue<uint8_t>(_trackDesign->entrance_style);
|
||||
tempStream.WriteValue<uint8_t>(_trackDesign->total_air_time);
|
||||
|
|
|
@ -1181,7 +1181,6 @@ int32_t ride_get_refund_price(const Ride* ride);
|
|||
int32_t ride_get_random_colour_preset_index(uint8_t ride_type);
|
||||
money32 ride_get_common_price(Ride* forRide);
|
||||
rct_ride_name get_ride_naming(const uint8_t rideType, rct_ride_entry* rideEntry);
|
||||
money32 ride_create_command(int32_t type, int32_t subType, int32_t flags, ride_id_t* outRideIndex, uint8_t* outRideColour);
|
||||
|
||||
void ride_clear_for_construction(Ride* ride);
|
||||
void ride_entrance_exit_place_provisional_ghost();
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
#include "../actions/LargeSceneryPlaceAction.hpp"
|
||||
#include "../actions/LargeSceneryRemoveAction.hpp"
|
||||
#include "../actions/MazePlaceTrackAction.hpp"
|
||||
#include "../actions/RideCreateAction.hpp"
|
||||
#include "../actions/RideEntranceExitPlaceAction.hpp"
|
||||
#include "../actions/RideSetName.hpp"
|
||||
#include "../actions/RideSetSetting.hpp"
|
||||
#include "../actions/RideSetVehiclesAction.hpp"
|
||||
#include "../actions/SmallSceneryPlaceAction.hpp"
|
||||
|
@ -28,6 +30,7 @@
|
|||
#include "../actions/WallPlaceAction.hpp"
|
||||
#include "../actions/WallRemoveAction.hpp"
|
||||
#include "../audio/audio.h"
|
||||
#include "../core/DataSerialiser.h"
|
||||
#include "../core/File.h"
|
||||
#include "../core/String.hpp"
|
||||
#include "../drawing/X8DrawingEngine.h"
|
||||
|
@ -79,14 +82,13 @@ LocationXYZ16 gTrackPreviewOrigin;
|
|||
|
||||
bool byte_9D8150;
|
||||
static uint8_t _trackDesignPlaceOperation;
|
||||
static bool _trackDesignDontPlaceScenery;
|
||||
static money32 _trackDesignPlaceCost;
|
||||
static int16_t _trackDesignPlaceZ;
|
||||
static int16_t _trackDesignPlaceSceneryZ;
|
||||
|
||||
// Previously all flags in byte_F4414E
|
||||
static bool _trackDesignPlaceStateEntranceExitPlaced = false;
|
||||
static bool _trackDesignPlaceStateSceneryUnavailable = false;
|
||||
bool _trackDesignPlaceStateSceneryUnavailable = false;
|
||||
static bool _trackDesignPlaceStateHasScenery = false;
|
||||
static bool _trackDesignPlaceStatePlaceScenery = true;
|
||||
|
||||
|
@ -558,6 +560,64 @@ rct_string_id TrackDesign::CreateTrackDesignScenery()
|
|||
return STR_NONE;
|
||||
}
|
||||
|
||||
void TrackDesign::Serialise(DataSerialiser& stream)
|
||||
{
|
||||
if (stream.IsLogging())
|
||||
{
|
||||
stream << DS_TAG(name);
|
||||
// There is too much information logged.
|
||||
// See sub actions for this information if required.
|
||||
return;
|
||||
}
|
||||
stream << DS_TAG(type);
|
||||
stream << DS_TAG(vehicle_type);
|
||||
stream << DS_TAG(cost);
|
||||
stream << DS_TAG(flags);
|
||||
stream << DS_TAG(ride_mode);
|
||||
stream << DS_TAG(track_flags);
|
||||
stream << DS_TAG(colour_scheme);
|
||||
stream << DS_TAG(vehicle_colours);
|
||||
stream << DS_TAG(entrance_style);
|
||||
stream << DS_TAG(total_air_time);
|
||||
stream << DS_TAG(depart_flags);
|
||||
stream << DS_TAG(number_of_trains);
|
||||
stream << DS_TAG(number_of_cars_per_train);
|
||||
stream << DS_TAG(min_waiting_time);
|
||||
stream << DS_TAG(max_waiting_time);
|
||||
stream << DS_TAG(operation_setting);
|
||||
stream << DS_TAG(max_speed);
|
||||
stream << DS_TAG(average_speed);
|
||||
stream << DS_TAG(ride_length);
|
||||
stream << DS_TAG(max_positive_vertical_g);
|
||||
stream << DS_TAG(max_negative_vertical_g);
|
||||
stream << DS_TAG(max_lateral_g);
|
||||
stream << DS_TAG(inversions);
|
||||
stream << DS_TAG(holes);
|
||||
stream << DS_TAG(drops);
|
||||
stream << DS_TAG(highest_drop_height);
|
||||
stream << DS_TAG(excitement);
|
||||
stream << DS_TAG(intensity);
|
||||
stream << DS_TAG(nausea);
|
||||
stream << DS_TAG(upkeep_cost);
|
||||
stream << DS_TAG(track_spine_colour);
|
||||
stream << DS_TAG(track_rail_colour);
|
||||
stream << DS_TAG(track_support_colour);
|
||||
stream << DS_TAG(flags2);
|
||||
stream << DS_TAG(vehicle_object);
|
||||
stream << DS_TAG(space_required_x);
|
||||
stream << DS_TAG(space_required_y);
|
||||
stream << DS_TAG(vehicle_additional_colour);
|
||||
stream << DS_TAG(lift_hill_speed);
|
||||
stream << DS_TAG(num_circuits);
|
||||
|
||||
stream << DS_TAG(maze_elements);
|
||||
stream << DS_TAG(track_elements);
|
||||
stream << DS_TAG(entrance_elements);
|
||||
stream << DS_TAG(scenery_elements);
|
||||
|
||||
stream << DS_TAG(name);
|
||||
}
|
||||
|
||||
std::unique_ptr<TrackDesign> track_design_open(const utf8* path)
|
||||
{
|
||||
try
|
||||
|
@ -1778,6 +1838,26 @@ int32_t place_virtual_track(
|
|||
return _trackDesignPlaceCost;
|
||||
}
|
||||
|
||||
static money32 track_design_ride_create_command(int32_t type, int32_t subType, int32_t flags, ride_id_t* outRideIndex)
|
||||
{
|
||||
// Don't set colours as will be set correctly later.
|
||||
auto gameAction = RideCreateAction(type, subType, 0, 0);
|
||||
gameAction.SetFlags(flags);
|
||||
|
||||
auto r = GameActions::ExecuteNested(&gameAction);
|
||||
const RideCreateGameActionResult* res = static_cast<RideCreateGameActionResult*>(r.get());
|
||||
|
||||
// Callee's of this function expect MONEY32_UNDEFINED in case of failure.
|
||||
if (res->Error != GA_ERROR::OK)
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
*outRideIndex = res->rideIndex;
|
||||
|
||||
return res->Cost;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006D2189
|
||||
|
@ -1796,9 +1876,8 @@ static bool track_design_place_preview(TrackDesign* td6, money32* cost, Ride** o
|
|||
}
|
||||
|
||||
ride_id_t rideIndex;
|
||||
uint8_t colour;
|
||||
uint8_t rideCreateFlags = GAME_COMMAND_FLAG_APPLY | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND;
|
||||
if (ride_create_command(td6->type, entry_index, rideCreateFlags, &rideIndex, &colour) == MONEY32_UNDEFINED)
|
||||
if (track_design_ride_create_command(td6->type, entry_index, rideCreateFlags, &rideIndex) == MONEY32_UNDEFINED)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -1881,195 +1960,6 @@ static bool track_design_place_preview(TrackDesign* td6, money32* cost, Ride** o
|
|||
}
|
||||
}
|
||||
|
||||
static money32 place_track_design(int16_t x, int16_t y, int16_t z, uint8_t flags, ride_id_t* outRideIndex)
|
||||
{
|
||||
*outRideIndex = RIDE_ID_NULL;
|
||||
|
||||
gCommandPosition.x = x + 16;
|
||||
gCommandPosition.y = y + 16;
|
||||
gCommandPosition.z = z;
|
||||
|
||||
if (!(flags & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED))
|
||||
{
|
||||
if (game_is_paused() && !gCheatsBuildInPauseMode)
|
||||
{
|
||||
gGameCommandErrorText = STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
TrackDesign* td6 = gActiveTrackDesign;
|
||||
if (td6 == nullptr)
|
||||
{
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
rct_object_entry* rideEntryObject = &td6->vehicle_object;
|
||||
|
||||
uint8_t entryType, entryIndex;
|
||||
if (!find_object_in_entry_group(rideEntryObject, &entryType, &entryIndex))
|
||||
{
|
||||
entryIndex = 0xFF;
|
||||
}
|
||||
// Force a fallback if the entry is not invented yet a td6 of it is selected, which can happen in select-by-track-type mode.
|
||||
else if (!ride_entry_is_invented(entryIndex) && !gCheatsIgnoreResearchStatus)
|
||||
{
|
||||
entryIndex = 0xFF;
|
||||
}
|
||||
|
||||
// The rest of the cases are handled by the code in ride_create()
|
||||
if (RideGroupManager::RideTypeHasRideGroups(td6->type) && entryIndex == 0xFF)
|
||||
{
|
||||
const ObjectRepositoryItem* ori = object_repository_find_object_by_name(rideEntryObject->name);
|
||||
if (ori != nullptr)
|
||||
{
|
||||
uint8_t rideGroupIndex = ori->RideInfo.RideGroupIndex;
|
||||
const RideGroup* td6RideGroup = RideGroupManager::RideGroupFind(td6->type, rideGroupIndex);
|
||||
|
||||
uint8_t* availableRideEntries = get_ride_entry_indices_for_ride_type(td6->type);
|
||||
for (uint8_t* rei = availableRideEntries; *rei != RIDE_ENTRY_INDEX_NULL; rei++)
|
||||
{
|
||||
rct_ride_entry* ire = get_ride_entry(*rei);
|
||||
|
||||
if (!ride_entry_is_invented(*rei) && !gCheatsIgnoreResearchStatus)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const RideGroup* irg = RideGroupManager::GetRideGroup(td6->type, ire);
|
||||
if (td6RideGroup->Equals(irg))
|
||||
{
|
||||
entryIndex = *rei;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ride_id_t rideIndex;
|
||||
uint8_t rideColour;
|
||||
money32 createRideResult = ride_create_command(td6->type, entryIndex, flags, &rideIndex, &rideColour);
|
||||
if (createRideResult == MONEY32_UNDEFINED)
|
||||
{
|
||||
gGameCommandErrorTitle = STR_CANT_CREATE_NEW_RIDE_ATTRACTION;
|
||||
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION;
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
auto ride = get_ride(rideIndex);
|
||||
if (ride == nullptr)
|
||||
{
|
||||
log_warning("Invalid game command for track placement, ride id = %d", rideIndex);
|
||||
return MONEY32_UNDEFINED;
|
||||
}
|
||||
|
||||
money32 cost = 0;
|
||||
if (!(flags & GAME_COMMAND_FLAG_APPLY))
|
||||
{
|
||||
_trackDesignDontPlaceScenery = false;
|
||||
cost = place_virtual_track(td6, PTD_OPERATION_PLACE_QUERY, true, ride, x, y, z);
|
||||
if (_trackDesignPlaceStateSceneryUnavailable)
|
||||
{
|
||||
_trackDesignDontPlaceScenery = true;
|
||||
cost = place_virtual_track(td6, PTD_OPERATION_PLACE_QUERY, false, ride, x, y, z);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t operation;
|
||||
if (flags & GAME_COMMAND_FLAG_GHOST)
|
||||
{
|
||||
operation = PTD_OPERATION_PLACE_GHOST;
|
||||
}
|
||||
else
|
||||
{
|
||||
operation = PTD_OPERATION_PLACE;
|
||||
}
|
||||
|
||||
cost = place_virtual_track(td6, operation, !_trackDesignDontPlaceScenery, ride, x, y, z);
|
||||
}
|
||||
|
||||
if (cost == MONEY32_UNDEFINED || !(flags & GAME_COMMAND_FLAG_APPLY))
|
||||
{
|
||||
rct_string_id error_reason = gGameCommandErrorText;
|
||||
ride_action_modify(ride, RIDE_MODIFY_DEMOLISH, flags);
|
||||
gGameCommandErrorText = error_reason;
|
||||
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION;
|
||||
*outRideIndex = ride->id;
|
||||
return cost;
|
||||
}
|
||||
|
||||
if (entryIndex != 0xFF)
|
||||
{
|
||||
auto colour = ride_get_unused_preset_vehicle_colour(entryIndex);
|
||||
auto rideSetVehicleAction = RideSetVehicleAction(ride->id, RideSetVehicleType::RideEntry, entryIndex, colour);
|
||||
flags& GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&rideSetVehicleAction)
|
||||
: GameActions::QueryNested(&rideSetVehicleAction);
|
||||
}
|
||||
|
||||
set_operating_setting_nested(ride->id, RideSetSetting::Mode, td6->ride_mode, flags);
|
||||
auto rideSetVehicleAction2 = RideSetVehicleAction(ride->id, RideSetVehicleType::NumTrains, td6->number_of_trains);
|
||||
flags& GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&rideSetVehicleAction2)
|
||||
: GameActions::QueryNested(&rideSetVehicleAction2);
|
||||
auto rideSetVehicleAction3 = RideSetVehicleAction(
|
||||
ride->id, RideSetVehicleType::NumCarsPerTrain, td6->number_of_cars_per_train);
|
||||
flags& GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&rideSetVehicleAction3)
|
||||
: GameActions::QueryNested(&rideSetVehicleAction3);
|
||||
set_operating_setting_nested(ride->id, RideSetSetting::Departure, td6->depart_flags, flags);
|
||||
set_operating_setting_nested(ride->id, RideSetSetting::MinWaitingTime, td6->min_waiting_time, flags);
|
||||
set_operating_setting_nested(ride->id, RideSetSetting::MaxWaitingTime, td6->max_waiting_time, flags);
|
||||
set_operating_setting_nested(ride->id, RideSetSetting::Operation, td6->operation_setting, flags);
|
||||
set_operating_setting_nested(ride->id, RideSetSetting::LiftHillSpeed, td6->lift_hill_speed & 0x1F, flags);
|
||||
|
||||
uint8_t num_circuits = td6->num_circuits;
|
||||
if (num_circuits == 0)
|
||||
{
|
||||
num_circuits = 1;
|
||||
}
|
||||
set_operating_setting_nested(ride->id, RideSetSetting::NumCircuits, num_circuits, flags);
|
||||
ride->SetToDefaultInspectionInterval();
|
||||
ride->lifecycle_flags |= RIDE_LIFECYCLE_NOT_CUSTOM_DESIGN;
|
||||
ride->colour_scheme_type = td6->colour_scheme;
|
||||
|
||||
ride->entrance_style = td6->entrance_style;
|
||||
|
||||
for (int32_t i = 0; i < RCT12_NUM_COLOUR_SCHEMES; i++)
|
||||
{
|
||||
ride->track_colour[i].main = td6->track_spine_colour[i];
|
||||
ride->track_colour[i].additional = td6->track_rail_colour[i];
|
||||
ride->track_colour[i].supports = td6->track_support_colour[i];
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < MAX_VEHICLES_PER_RIDE; i++)
|
||||
{
|
||||
ride->vehicle_colours[i].Body = td6->vehicle_colours[i].body_colour;
|
||||
ride->vehicle_colours[i].Trim = td6->vehicle_colours[i].trim_colour;
|
||||
ride->vehicle_colours[i].Ternary = td6->vehicle_additional_colour[i];
|
||||
}
|
||||
|
||||
ride_set_name(ride, td6->name.c_str(), flags);
|
||||
|
||||
gCommandExpenditureType = RCT_EXPENDITURE_TYPE_RIDE_CONSTRUCTION;
|
||||
*outRideIndex = ride->id;
|
||||
return cost;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x006D13FE
|
||||
*/
|
||||
void game_command_place_track_design(
|
||||
int32_t* eax, int32_t* ebx, int32_t* ecx, [[maybe_unused]] int32_t* edx, [[maybe_unused]] int32_t* esi, int32_t* edi,
|
||||
[[maybe_unused]] int32_t* ebp)
|
||||
{
|
||||
int16_t x = *eax & 0xFFFF;
|
||||
int16_t y = *ecx & 0xFFFF;
|
||||
int16_t z = *edi & 0xFFFF;
|
||||
uint8_t flags = *ebx;
|
||||
ride_id_t rideIndex;
|
||||
*ebx = place_track_design(x, y, z, flags, &rideIndex);
|
||||
*edi = rideIndex;
|
||||
}
|
||||
|
||||
#pragma region Track Design Preview
|
||||
|
||||
/**
|
||||
|
|
|
@ -77,6 +77,8 @@ struct TrackDesignMazeElement
|
|||
};
|
||||
};
|
||||
|
||||
class DataSerialiser;
|
||||
|
||||
struct TrackDesign
|
||||
{
|
||||
uint8_t type;
|
||||
|
@ -86,7 +88,7 @@ struct TrackDesign
|
|||
uint8_t ride_mode;
|
||||
uint8_t track_flags;
|
||||
uint8_t colour_scheme;
|
||||
rct_vehicle_colour vehicle_colours[RCT2_MAX_CARS_PER_TRAIN];
|
||||
std::array<rct_vehicle_colour, RCT2_MAX_CARS_PER_TRAIN> vehicle_colours;
|
||||
uint8_t entrance_style;
|
||||
uint8_t total_air_time;
|
||||
uint8_t depart_flags;
|
||||
|
@ -130,6 +132,7 @@ struct TrackDesign
|
|||
public:
|
||||
rct_string_id CreateTrackDesign(const Ride& ride);
|
||||
rct_string_id CreateTrackDesignScenery();
|
||||
void Serialise(DataSerialiser& stream);
|
||||
|
||||
private:
|
||||
uint8_t _saveDirection;
|
||||
|
@ -195,6 +198,7 @@ extern LocationXYZ16 gTrackPreviewOrigin;
|
|||
|
||||
extern bool byte_9D8150;
|
||||
|
||||
extern bool _trackDesignPlaceStateSceneryUnavailable;
|
||||
extern bool gTrackDesignSaveMode;
|
||||
extern ride_id_t gTrackDesignSaveRideIndex;
|
||||
|
||||
|
@ -205,9 +209,6 @@ void track_design_mirror(TrackDesign* td6);
|
|||
int32_t place_virtual_track(
|
||||
TrackDesign* td6, uint8_t ptdOperation, bool placeScenery, Ride* ride, int16_t x, int16_t y, int16_t z);
|
||||
|
||||
void game_command_place_track_design(
|
||||
int32_t* eax, int32_t* ebx, int32_t* ecx, int32_t* edx, int32_t* esi, int32_t* edi, int32_t* ebp);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Track design preview
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -102,4 +102,6 @@ enum
|
|||
INTENT_ACTION_UPDATE_CASH,
|
||||
INTENT_ACTION_UPDATE_BANNER,
|
||||
INTENT_ACTION_UPDATE_RESEARCH,
|
||||
INTENT_ACTION_TRACK_DESIGN_REMOVE_PROVISIONAL,
|
||||
INTENT_ACTION_TRACK_DESIGN_RESTORE_PROVISIONAL,
|
||||
};
|
||||
|
|
|
@ -359,6 +359,12 @@ struct CoordsXYZD : public CoordsXYZ
|
|||
{
|
||||
}
|
||||
|
||||
constexpr CoordsXYZD(CoordsXYZ _c, Direction _d)
|
||||
: CoordsXYZ(_c)
|
||||
, direction(_d)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const CoordsXYZD& other) const
|
||||
{
|
||||
return x == other.x && y == other.y && z == other.z && direction == other.direction;
|
||||
|
|
|
@ -1498,6 +1498,13 @@ void map_remove_provisional_elements()
|
|||
ride_remove_provisional_track_piece();
|
||||
ride_entrance_exit_remove_ghost();
|
||||
}
|
||||
// This is in non performant so only make network games suffer for it
|
||||
// non networked games do not need this as its to prevent desyncs.
|
||||
if ((network_get_mode() != NETWORK_MODE_NONE) && window_find_by_class(WC_TRACK_DESIGN_PLACE) != nullptr)
|
||||
{
|
||||
auto intent = Intent(INTENT_ACTION_TRACK_DESIGN_REMOVE_PROVISIONAL);
|
||||
context_broadcast_intent(&intent);
|
||||
}
|
||||
}
|
||||
|
||||
void map_restore_provisional_elements()
|
||||
|
@ -1514,6 +1521,13 @@ void map_restore_provisional_elements()
|
|||
ride_restore_provisional_track_piece();
|
||||
ride_entrance_exit_place_provisional_ghost();
|
||||
}
|
||||
// This is in non performant so only make network games suffer for it
|
||||
// non networked games do not need this as its to prevent desyncs.
|
||||
if ((network_get_mode() != NETWORK_MODE_NONE) && window_find_by_class(WC_TRACK_DESIGN_PLACE) != nullptr)
|
||||
{
|
||||
auto intent = Intent(INTENT_ACTION_TRACK_DESIGN_RESTORE_PROVISIONAL);
|
||||
context_broadcast_intent(&intent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue