Implement constructTrack (#1109)

This commit is contained in:
Duncan 2021-08-16 19:47:02 +01:00 committed by GitHub
parent f4f3d0d72c
commit 191e6d567c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 247 additions and 9 deletions

View File

@ -53,7 +53,7 @@ namespace OpenLoco::GameCommands
{ GameCommand::vehiclePassSignal, nullptr, 0x004B0B50, true },
{ GameCommand::vehicleCreate, Vehicles::create, 0x004AE5E4, true },
{ GameCommand::vehicleSell, nullptr, 0x004AED34, true },
{ GameCommand::gc_unk_7, nullptr, 0x0049BB98, true },
{ GameCommand::createTrack, nullptr, 0x0049BB98, true },
{ GameCommand::gc_unk_8, nullptr, 0x0049C7F2, true },
{ GameCommand::changeLoan, nullptr, 0x0046DE88, false },
{ GameCommand::vehicleRename, Vehicles::rename, 0x004B6572, false },
@ -84,7 +84,7 @@ namespace OpenLoco::GameCommands
{ GameCommand::vehicleOrderInsert, nullptr, 0x0047036E, false },
{ GameCommand::vehicleOrderDelete, nullptr, 0x0047057A, false },
{ GameCommand::vehicleOrderSkip, Vehicles::orderSkip, 0x0047071A, false },
{ GameCommand::gc_unk_38, nullptr, 0x00475FBC, true },
{ GameCommand::createRoad, nullptr, 0x00475FBC, true },
{ GameCommand::removeRoad, nullptr, 0x004775A5, true },
{ GameCommand::createRoadMod, nullptr, 0x0047A21E, true },
{ GameCommand::removeRoadMod, nullptr, 0x0047A42F, true },

View File

@ -40,7 +40,7 @@ namespace OpenLoco::GameCommands
vehiclePassSignal = 4,
vehicleCreate = 5,
vehicleSell = 6,
gc_unk_7 = 7,
createTrack = 7,
gc_unk_8 = 8,
changeLoan = 9,
vehicleRename = 10,
@ -71,7 +71,7 @@ namespace OpenLoco::GameCommands
vehicleOrderInsert = 35,
vehicleOrderDelete = 36,
vehicleOrderSkip = 37,
gc_unk_38 = 38,
createRoad = 38,
removeRoad = 39,
createRoadMod = 40,
removeRoadMod = 41,
@ -240,6 +240,42 @@ namespace OpenLoco::GameCommands
doCommand(GameCommand::vehicleSell, regs);
}
struct TrackPlacementArgs
{
static constexpr auto command = GameCommand::createTrack;
TrackPlacementArgs() = default;
explicit TrackPlacementArgs(const registers& regs)
: pos(regs.ax, regs.cx, regs.di)
, rotation(regs.bh & 0x3)
, trackId(regs.dh & 0x3F)
, mods(regs.di >> 16)
, bridge(regs.edx >> 24)
, trackObjectId(regs.dl)
, unk(regs.edi & 0x800000)
{
}
Map::Pos3 pos;
uint8_t rotation;
uint8_t trackId;
uint8_t mods;
uint8_t bridge;
uint8_t trackObjectId;
bool unk;
explicit operator registers() const
{
registers regs;
regs.eax = pos.x;
regs.cx = pos.y;
regs.edi = pos.z | (mods << 16) | (unk ? 0x800000 : 0);
regs.bh = rotation;
regs.edx = trackObjectId | (trackId << 8) | (bridge << 24);
return regs;
}
};
// Change loan
inline void do_9(currency32_t newLoan)
{
@ -727,6 +763,40 @@ namespace OpenLoco::GameCommands
return doCommand(GameCommand::vehicleOrderSkip, regs);
}
struct RoadPlacementArgs
{
static constexpr auto command = GameCommand::createRoad;
RoadPlacementArgs() = default;
explicit RoadPlacementArgs(const registers& regs)
: pos(regs.ax, regs.cx, regs.di)
, rotation(regs.bh & 0x3)
, roadId(regs.dh & 0xF)
, mods(regs.di >> 16)
, bridge(regs.edx >> 24)
, roadObjectId(regs.dl)
{
}
Map::Pos3 pos;
uint8_t rotation;
uint8_t roadId;
uint8_t mods;
uint8_t bridge;
uint8_t roadObjectId;
explicit operator registers() const
{
registers regs;
regs.eax = pos.x;
regs.cx = pos.y;
regs.edi = pos.z | (mods << 16);
regs.bh = rotation;
regs.edx = roadObjectId | (roadId << 8) | (bridge << 24);
return regs;
}
};
struct RoadRemovalArgs
{
static constexpr auto command = GameCommand::removeRoad;

View File

@ -142,9 +142,13 @@ namespace OpenLoco::StringIds
constexpr string_id cant_build_signals_here = 140;
constexpr string_id cant_remove_signal = 141;
constexpr string_id cant_build_pop3_string = 143;
constexpr string_id menu_underground_view = 145;
constexpr string_id menu_hide_foreground_tracks_roads = 146;
constexpr string_id unable_to_cross_or_create_junction_with_string = 156;
constexpr string_id capt_signal = 158;
constexpr string_id capt_station = 159;
constexpr string_id capt_airport = 160;

View File

@ -29,6 +29,8 @@ namespace OpenLoco::Ui::Windows::Construction::Construction
static loco_global<uint32_t, 0x00523394> _toolWidgetIndex;
static loco_global<int32_t, 0x00E3F0B8> gCurrentRotation;
static loco_global<uint8_t, 0x0112C2E9> _alternateTrackObjectId; // set from GameCommands::createRoad
static loco_global<uint8_t[18], 0x0050A006> available_objects; // toptoolbar
namespace TrackPiece
{
@ -131,13 +133,175 @@ namespace OpenLoco::Ui::Windows::Construction::Construction
_pickupDirection = 0;
}
// 0x004A18D4
static void sub_4A18D4()
{
if (_alternateTrackObjectId == 0xFF)
{
return;
}
if ((_alternateTrackObjectId | (1 << 7)) == _trackType)
{
return;
}
auto* alternativeRoadObj = ObjectManager::get<RoadObject>(_alternateTrackObjectId);
if (!(alternativeRoadObj->flags & Flags12::unk_03))
{
return;
}
auto* curRoadObj = ObjectManager::get<RoadObject>(_trackType & ~(1 << 7));
if (!(curRoadObj->flags & Flags12::unk_03))
{
return;
}
for (const auto objId : available_objects)
{
if (objId == 0xFF)
{
return;
}
if (objId == (_alternateTrackObjectId | (1 << 7)))
{
_trackType = _alternateTrackObjectId | (1 << 7);
Common::sub_4A3A50();
}
}
}
// 0x0049FA10
static void constructRoad()
{
_trackCost = 0x80000000;
_byte_1136076 = 0;
_dword_1135F42 = 0x80000000;
removeConstructionGhosts();
auto roadPiece = getRoadPieceId(_lastSelectedTrackPiece, _lastSelectedTrackGradient, _constructionRotation);
if (!roadPiece)
{
return;
}
auto* roadObj = ObjectManager::get<RoadObject>(_trackType & ~(1 << 7));
auto formatArgs = FormatArguments::common();
formatArgs.skip(3 * sizeof(string_id));
formatArgs.push(roadObj->name);
GameCommands::setErrorTitle(StringIds::cant_build_pop3_string);
GameCommands::RoadPlacementArgs args;
args.pos = Map::Pos3(_x, _y, _constructionZ);
args.rotation = roadPiece->rotation;
args.roadId = roadPiece->id;
args.mods = _lastSelectedMods;
args.bridge = _lastSelectedBridge;
args.roadObjectId = _trackType & ~(1 << 7);
_dword_1135F42 = GameCommands::doCommand(args, GameCommands::Flags::apply);
if (_dword_1135F42 == GameCommands::FAILURE)
{
if (GameCommands::getErrorText() != StringIds::unable_to_cross_or_create_junction_with_string
|| _alternateTrackObjectId == 0xFF)
{
return;
}
sub_4A18D4();
roadPiece = getRoadPieceId(_lastSelectedTrackPiece, _lastSelectedTrackGradient, _constructionRotation);
if (!roadPiece)
{
return;
}
WindowManager::close(WindowType::error);
args.pos = Map::Pos3(_x, _y, _constructionZ);
args.rotation = roadPiece->rotation;
args.roadId = roadPiece->id;
args.mods = _lastSelectedMods;
args.bridge = _lastSelectedBridge;
args.roadObjectId = _trackType & ~(1 << 7);
_dword_1135F42 = GameCommands::doCommand(args, GameCommands::Flags::apply);
}
if (_dword_1135F42 != GameCommands::FAILURE)
{
const auto& trackSize = TrackData::getUnkRoad((args.roadId << 3) | (args.rotation & 0x3));
const auto newPosition = args.pos + trackSize.pos;
_x = newPosition.x;
_y = newPosition.y;
_constructionZ = newPosition.z;
_constructionRotation = trackSize.rotationEnd;
_byte_522096 = 0;
_byte_1136066 = 0;
if (_lastSelectedTrackPiece >= 9)
{
_lastSelectedTrackPiece = 0;
}
}
}
// 0x0049F93A
static void constructTrack()
{
_trackCost = 0x80000000;
_byte_1136076 = 0;
_dword_1135F42 = 0x80000000;
removeConstructionGhosts();
auto trackPiece = getTrackPieceId(_lastSelectedTrackPiece, _lastSelectedTrackGradient, _constructionRotation);
if (!trackPiece)
{
return;
}
auto* roadObj = ObjectManager::get<TrackObject>(_trackType);
auto formatArgs = FormatArguments::common();
formatArgs.skip(3 * sizeof(string_id));
formatArgs.push(roadObj->name);
GameCommands::setErrorTitle(StringIds::cant_build_pop3_string);
GameCommands::TrackPlacementArgs args;
args.pos = Map::Pos3(_x, _y, _constructionZ);
args.rotation = trackPiece->rotation;
args.trackId = trackPiece->id;
args.mods = _lastSelectedMods;
args.bridge = _lastSelectedBridge;
args.trackObjectId = _trackType;
args.unk = _byte_113607E & (1 << 0);
_dword_1135F42 = GameCommands::doCommand(args, GameCommands::Flags::apply);
if (_dword_1135F42 == GameCommands::FAILURE)
{
return;
}
const auto& trackSize = TrackData::getUnkTrack((args.trackId << 3) | (args.rotation & 0x3));
const auto newPosition = args.pos + trackSize.pos;
_x = newPosition.x;
_y = newPosition.y;
_constructionZ = newPosition.z;
_constructionRotation = trackSize.rotationEnd;
_byte_522096 = 0;
_byte_1136066 = 0;
if (_lastSelectedTrackPiece >= 9)
{
_lastSelectedTrackPiece = 0;
}
}
// 0x0049F92D
static void constructTrack(Window* self, WidgetIndex_t widgetIndex)
{
registers regs;
regs.edx = widgetIndex;
regs.esi = X86Pointer(self);
call(0x0049F92D, regs);
if (_trackType & (1 << 7))
{
constructRoad();
}
else
{
constructTrack();
}
activateSelectedConstructionWidgets();
}
// 0x004A0121

View File

@ -335,7 +335,7 @@ namespace OpenLoco::Ui::Windows::ToolbarTop::Game
// 0x0043A2B0
static void railroadMenuMouseDown(Window* window, WidgetIndex_t widgetIndex)
{
// Load objects.
// Load dropdown objects removing any that are not unlocked.
registers regs;
regs.edi = X86Pointer(&available_objects[0]);
call(0x004A6841, regs);