Merge pull request #10189 from duncanspumpkin/track_ac

Track Design GameAction
This commit is contained in:
Duncan 2019-12-15 17:10:57 +00:00 committed by GitHub
commit b767351a56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 820 additions and 545 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -218,7 +218,6 @@ void GameState::Update()
pause_toggle();
}
gGameCommandNestLevel = 0;
gDoSingleUpdate = false;
gInUpdateCode = false;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -46,6 +46,11 @@ public:
return !_isSaving;
}
bool IsLogging() const
{
return _isLogging;
}
IStream& GetStream()
{
return _activeStream;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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