Start moving track design into a game action

Make place track design work with game actions
This commit is contained in:
duncanspumpkin 2019-10-19 14:22:44 +01:00
parent 9d09f1b95c
commit c781fcb72d
6 changed files with 360 additions and 17 deletions

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>
@ -446,23 +447,19 @@ 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)
{
int32_t eax, ebx, ecx, edx, esi, edi, ebp;
money32 result;
auto tdAction = TrackDesignAction({ x, y, z }, *_trackDesign);
tdAction.SetFlags(bl);
auto res = (bl & GAME_COMMAND_FLAG_APPLY) ? GameActions::Execute(&tdAction) : GameActions::Query(&tdAction);
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;
if (res->Error != GA_ERROR::OK)
{
*cost = MONEY32_UNDEFINED;
}
else
{
*cost = res->Cost;
}
*rideIndex = dynamic_cast<TrackDesignActionResult*>(res.get())->rideIndex;
}
/**

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

@ -0,0 +1,275 @@
/*****************************************************************************
* 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 "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;
if (!(GetFlags() & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED))
{
if (game_is_paused() && !gCheatsBuildInPauseMode)
{
return MakeResult(GA_ERROR::GAME_PAUSED, STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED);
}
}
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;
}
}
}
}
ride_id_t rideIndex;
uint8_t rideColour;
money32 createRideResult = ride_create_command(_td.type, entryIndex, GetFlags(), &rideIndex, &rideColour);
if (createRideResult == MONEY32_UNDEFINED)
{
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;
ride_action_modify(ride, RIDE_MODIFY_DEMOLISH, GetFlags());
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;
if (!(GetFlags() & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED))
{
if (game_is_paused() && !gCheatsBuildInPauseMode)
{
return MakeResult(GA_ERROR::GAME_PAUSED, STR_CONSTRUCTION_NOT_POSSIBLE_WHILE_GAME_IS_PAUSED);
}
}
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;
}
}
}
}
ride_id_t rideIndex;
uint8_t rideColour;
money32 createRideResult = ride_create_command(_td.type, entryIndex, GetFlags(), &rideIndex, &rideColour);
if (createRideResult == MONEY32_UNDEFINED)
{
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;
ride_action_modify(ride, RIDE_MODIFY_DEMOLISH, GetFlags());
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];
}
ride_set_name(ride, _td.name.c_str(), GAME_COMMAND_FLAG_APPLY);
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 "GameAction.h"
#include "../ride/TrackDesign.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:
CoordsXYZ _loc;
TrackDesign _td;
public:
TrackDesignAction()
{
}
TrackDesignAction(CoordsXYZ location, const TrackDesign &td)
: _loc(location)
, _td(td)
{
}
uint16_t GetActionFlags() const override
{
return GameActionBase::GetActionFlags() | GA_FLAGS::CLIENT_ONLY;
}
void Serialise(DataSerialiser & stream) override
{
GameAction::Serialise(stream);
stream << DS_TAG(_loc);
}
GameActionResult::Ptr Query() const override;
GameActionResult::Ptr Execute() const override;
};

View file

@ -86,7 +86,7 @@ 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;

View file

@ -195,6 +195,7 @@ extern LocationXYZ16 gTrackPreviewOrigin;
extern bool byte_9D8150;
extern bool _trackDesignPlaceStateSceneryUnavailable;
extern bool gTrackDesignSaveMode;
extern ride_id_t gTrackDesignSaveRideIndex;