Track/Road Mods Tool Update/Down (#1092)

* Implement road overhead tool down

* Implement track mod tool down

* Implement overhead tool update

* Apply review comments
This commit is contained in:
Duncan 2021-08-09 13:04:41 +01:00 committed by GitHub
parent b08815aeaf
commit 99049e5fab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 320 additions and 20 deletions

View File

@ -63,8 +63,8 @@ namespace OpenLoco::GameCommands
{ GameCommand::gc_unk_14, nullptr, 0x004891E4, true },
{ GameCommand::gc_unk_15, nullptr, 0x0048BB20, true },
{ GameCommand::removeTrackStation, nullptr, 0x0048C402, true },
{ GameCommand::gc_unk_17, nullptr, 0x004A6479, true },
{ GameCommand::gc_unk_18, nullptr, 0x004A668A, true },
{ GameCommand::createTrackMod, nullptr, 0x004A6479, true },
{ GameCommand::removeTrackMod, nullptr, 0x004A668A, true },
{ GameCommand::changeCompanyColourScheme, changeCompanyColour, 0x0043483D, false },
{ GameCommand::pauseGame, togglePause, 0x00431E32, false },
{ GameCommand::loadSaveQuitGame, loadSaveQuit, 0x0043BFCB, false },
@ -86,8 +86,8 @@ namespace OpenLoco::GameCommands
{ GameCommand::vehicleOrderSkip, Vehicles::orderSkip, 0x0047071A, false },
{ GameCommand::gc_unk_38, nullptr, 0x00475FBC, true },
{ GameCommand::gc_unk_39, nullptr, 0x004775A5, true },
{ GameCommand::gc_unk_40, nullptr, 0x0047A21E, true },
{ GameCommand::gc_unk_41, nullptr, 0x0047A42F, true },
{ GameCommand::createRoadMod, nullptr, 0x0047A21E, true },
{ GameCommand::removeRoadMod, nullptr, 0x0047A42F, true },
{ GameCommand::gc_unk_42, nullptr, 0x0048C708, true },
{ GameCommand::removeRoadStation, nullptr, 0x0048D2AC, true },
{ GameCommand::createBuilding, nullptr, 0x0042D133, true },

View File

@ -50,8 +50,8 @@ namespace OpenLoco::GameCommands
gc_unk_14 = 14,
gc_unk_15 = 15,
removeTrackStation = 16,
gc_unk_17 = 17,
gc_unk_18 = 18,
createTrackMod = 17,
removeTrackMod = 18,
changeCompanyColourScheme = 19,
pauseGame = 20,
loadSaveQuitGame = 21,
@ -73,8 +73,8 @@ namespace OpenLoco::GameCommands
vehicleOrderSkip = 37,
gc_unk_38 = 38,
gc_unk_39 = 39,
gc_unk_40 = 40,
gc_unk_41 = 41,
createRoadMod = 40,
removeRoadMod = 41,
gc_unk_42 = 42,
removeRoadStation = 43,
createBuilding = 44,
@ -361,6 +361,49 @@ namespace OpenLoco::GameCommands
return doCommand(GameCommand::removeTrackStation, regs) != FAILURE;
}
struct TrackModsPlacementArgs
{
TrackModsPlacementArgs() = default;
explicit TrackModsPlacementArgs(const registers& regs)
: pos(regs.ax, regs.cx, regs.di)
, rotation(regs.bh & 0x3)
, trackId(regs.dl & 0x3F)
, index(regs.dh & 0x3)
, type((regs.edi >> 16) & 0xF)
, trackObjType(regs.ebp & 0xFF)
, modSection((regs.ebp >> 16) & 0xFF)
{
}
Map::Pos3 pos;
uint8_t rotation;
uint8_t trackId;
uint8_t index;
uint8_t type;
uint8_t trackObjType;
uint8_t modSection;
explicit operator registers() const
{
registers regs;
regs.ax = pos.x;
regs.cx = pos.y;
regs.bh = rotation;
regs.dl = trackId;
regs.dh = index;
regs.edi = pos.z | (type << 16);
regs.ebp = trackObjType | (modSection << 16);
return regs;
}
};
inline uint32_t do_17(uint8_t flags, const TrackModsPlacementArgs& args)
{
registers regs = registers(args);
regs.bl = flags;
return doCommand(GameCommand::createTrackMod, regs);
}
// Change company colour scheme
inline void do_19(int8_t isPrimary, int8_t value, int8_t colourType, int8_t setColourMode, uint8_t companyId)
{
@ -678,6 +721,49 @@ namespace OpenLoco::GameCommands
return doCommand(GameCommand::vehicleOrderSkip, regs);
}
struct RoadModsPlacementArgs
{
RoadModsPlacementArgs() = default;
explicit RoadModsPlacementArgs(const registers& regs)
: pos(regs.ax, regs.cx, regs.di)
, rotation(regs.bh & 0x3)
, roadId(regs.dl & 0xF)
, index(regs.dh & 0x3)
, type((regs.edi >> 16) & 0xF)
, roadObjType(regs.ebp & 0xFF)
, modSection((regs.ebp >> 16) & 0xFF)
{
}
Map::Pos3 pos;
uint8_t rotation;
uint8_t roadId;
uint8_t index;
uint8_t type;
uint8_t roadObjType;
uint8_t modSection;
explicit operator registers() const
{
registers regs;
regs.ax = pos.x;
regs.cx = pos.y;
regs.bh = rotation;
regs.dl = roadId;
regs.dh = index;
regs.edi = pos.z | (type << 16);
regs.ebp = roadObjType | (modSection << 16);
return regs;
}
};
inline uint32_t do_40(uint8_t flags, const RoadModsPlacementArgs& args)
{
registers regs = registers(args);
regs.bl = flags;
return doCommand(GameCommand::createRoadMod, regs);
}
struct RoadStationRemovalArgs
{
RoadStationRemovalArgs() = default;

View File

@ -152,6 +152,7 @@ namespace OpenLoco::StringIds
constexpr string_id cant_remove_airport = 164;
constexpr string_id cant_remove_ship_port = 165;
constexpr string_id cant_remove_station = 166;
constexpr string_id wrong_type_of_track_road = 167;
constexpr string_id too_many_objects_in_game = 171;
constexpr string_id menu_rotate_clockwise = 172;

View File

@ -50,8 +50,10 @@ namespace OpenLoco::Ui::Windows::Construction
static loco_global<uint16_t, 0x01135FD6> _word_1135FD6;
static loco_global<uint16_t, 0x01135FD8> _word_1135FD8;
static loco_global<uint16_t, 0x01135FE4> _lastSelectedMods;
static loco_global<Map::Pos3, 0x01135FF8> _modGhostPos;
static loco_global<uint16_t, 0x01135FFE> _word_1135FFE;
static loco_global<int16_t, 0x01136000> _word_1136000;
static loco_global<uint8_t, 0x01136010> _modGhostTrackObjId;
static loco_global<uint8_t[17], 0x0113601D> _signalList;
static loco_global<uint8_t, 0x0113602E> _lastSelectedSignal;
static loco_global<uint8_t, 0x0113602F> _isSignalBothDirections;
@ -61,6 +63,9 @@ namespace OpenLoco::Ui::Windows::Construction
static loco_global<uint8_t[17], 0x0113603B> _stationList;
static loco_global<uint8_t, 0x0113604C> _lastSelectedStationType;
static loco_global<uint8_t[4], 0x01136054> _modList;
static loco_global<uint8_t, 0x01136058> _modGhostRotation;
static loco_global<uint8_t, 0x01136059> _modGhostTrackId;
static loco_global<uint8_t, 0x0113605A> _modGhostTileIndex;
static loco_global<uint8_t, 0x0113605D> _makeJunction;
static loco_global<uint8_t, 0x01136061> _constructionHover;
static loco_global<uint8_t, 0x01136062> _trackType;

View File

@ -1,4 +1,6 @@
#include "../../Audio/Audio.h"
#include "../../CompanyManager.h"
#include "../../GameCommands/GameCommands.h"
#include "../../Graphics/ImageIds.h"
#include "../../Input.h"
#include "../../Localisation/FormatArguments.hpp"
@ -121,26 +123,232 @@ namespace OpenLoco::Ui::Windows::Construction::Overhead
Common::onUpdate(self, (1 << 5));
}
static std::optional<GameCommands::RoadModsPlacementArgs> getRoadModsPlacementArgsFromCursor(const int16_t x, const int16_t y)
{
static loco_global<Ui::Point, 0x0113600C> _113600C;
static loco_global<Viewport*, 0x01135F52> _1135F52;
_113600C = { x, y };
auto [interaction, viewport] = ViewportInteraction::getMapCoordinatesFromPos(x, y, ~(ViewportInteraction::InteractionItemFlags::roadAndTram));
_1135F52 = viewport;
if (interaction.type != ViewportInteraction::InteractionItem::road)
{
return std::nullopt;
}
auto* elRoad = reinterpret_cast<Map::TileElement*>(interaction.object)->asRoad();
if (elRoad == nullptr)
{
return std::nullopt;
}
GameCommands::RoadModsPlacementArgs args;
args.type = _lastSelectedMods;
args.pos = Map::Pos3(interaction.pos.x, interaction.pos.y, elRoad->baseZ() * 4);
args.rotation = elRoad->unkDirection();
args.roadId = elRoad->roadId();
args.index = elRoad->sequenceIndex();
args.roadObjType = elRoad->roadObjectId();
args.modSection = _lastSelectedTrackModSection;
return { args };
}
static std::optional<GameCommands::TrackModsPlacementArgs> getTrackModsPlacementArgsFromCursor(const int16_t x, const int16_t y)
{
static loco_global<Ui::Point, 0x0113600C> _113600C;
static loco_global<Viewport*, 0x01135F52> _1135F52;
_113600C = { x, y };
auto [interaction, viewport] = ViewportInteraction::getMapCoordinatesFromPos(x, y, ~(ViewportInteraction::InteractionItemFlags::track));
_1135F52 = viewport;
if (interaction.type != ViewportInteraction::InteractionItem::track)
{
return std::nullopt;
}
auto* elTrack = reinterpret_cast<Map::TileElement*>(interaction.object)->asTrack();
if (elTrack == nullptr)
{
return std::nullopt;
}
GameCommands::TrackModsPlacementArgs args;
args.type = _lastSelectedMods;
args.pos = Map::Pos3(interaction.pos.x, interaction.pos.y, elTrack->baseZ() * 4);
args.rotation = elTrack->unkDirection();
args.trackId = elTrack->trackId();
args.index = elTrack->sequenceIndex();
args.trackObjType = elTrack->trackObjectId();
args.modSection = _lastSelectedTrackModSection;
return { args };
}
static uint32_t placeRoadModGhost(const GameCommands::RoadModsPlacementArgs& args)
{
auto res = GameCommands::do_40(GameCommands::Flags::apply | GameCommands::Flags::flag_1 | GameCommands::Flags::flag_3 | GameCommands::Flags::flag_5 | GameCommands::Flags::flag_6, args);
if (res != GameCommands::FAILURE)
{
_byte_522096 = _byte_522096 | (1 << 4);
_modGhostPos = args.pos;
_modGhostRotation = args.rotation;
_modGhostTrackId = args.roadId;
_modGhostTileIndex = args.index;
_modGhostTrackObjId = args.roadObjType | (1 << 7); // This looks wrong!
}
return res;
}
static uint32_t placeTrackModGhost(const GameCommands::TrackModsPlacementArgs& args)
{
auto res = GameCommands::do_17(GameCommands::Flags::apply | GameCommands::Flags::flag_1 | GameCommands::Flags::flag_3 | GameCommands::Flags::flag_5 | GameCommands::Flags::flag_6, args);
if (res != GameCommands::FAILURE)
{
_byte_522096 = _byte_522096 | (1 << 4);
_modGhostPos = args.pos;
_modGhostRotation = args.rotation;
_modGhostTrackId = args.trackId;
_modGhostTileIndex = args.index;
_modGhostTrackObjId = args.trackObjType;
}
return res;
}
// 0x0049EC15
static void onToolUpdate(Window& self, const WidgetIndex_t widgetIndex, const int16_t x, const int16_t y)
{
registers regs;
regs.esi = X86Pointer(&self);
regs.dx = widgetIndex;
regs.ax = x;
regs.bx = y;
call(0x0049EC15, regs);
if (widgetIndex != widx::image)
{
return;
}
if (_trackType & (1 << 7))
{
auto placementArgs = getRoadModsPlacementArgsFromCursor(x, y);
if (!placementArgs || ((placementArgs->roadObjType | (1 << 7)) != _trackType))
{
removeConstructionGhosts();
if (_modCost != 0x80000000)
{
_modCost = 0x80000000;
self.invalidate();
}
return;
}
if (_byte_522096 & (1 << 4))
{
if (*_modGhostPos == placementArgs->pos
&& _modGhostRotation == placementArgs->rotation
&& _modGhostTrackId == placementArgs->roadId
&& _modGhostTileIndex == placementArgs->index
&& _modGhostTrackObjId == placementArgs->roadObjType)
{
return;
}
}
removeConstructionGhosts();
auto cost = placeRoadModGhost(*placementArgs);
if (cost != _modCost)
{
_modCost = cost;
self.invalidate();
}
}
else
{
auto placementArgs = getTrackModsPlacementArgsFromCursor(x, y);
if (!placementArgs || (placementArgs->trackObjType != _trackType))
{
removeConstructionGhosts();
if (_modCost != 0x80000000)
{
_modCost = 0x80000000;
self.invalidate();
}
return;
}
if (_byte_522096 & (1 << 4))
{
if (*_modGhostPos == placementArgs->pos
&& _modGhostRotation == placementArgs->rotation
&& _modGhostTrackId == placementArgs->trackId
&& _modGhostTileIndex == placementArgs->index
&& _modGhostTrackObjId == placementArgs->trackObjType)
{
return;
}
}
removeConstructionGhosts();
auto cost = placeTrackModGhost(*placementArgs);
if (cost != _modCost)
{
_modCost = cost;
self.invalidate();
}
}
}
// 0x0049EC20
static void onToolDown(Window& self, const WidgetIndex_t widgetIndex, const int16_t x, const int16_t y)
{
registers regs;
regs.esi = X86Pointer(&self);
regs.dx = widgetIndex;
regs.ax = x;
regs.bx = y;
call(0x0049EC20, regs);
if (widgetIndex != widx::image)
{
return;
}
removeConstructionGhosts();
if (_trackType & (1 << 7))
{
auto args = getRoadModsPlacementArgsFromCursor(x, y);
if (!args)
{
return;
}
if ((args->roadObjType | (1 << 7)) != _trackType)
{
Error::open(StringIds::error_cant_build_this_here, StringIds::wrong_type_of_track_road);
return;
}
GameCommands::setErrorTitle(StringIds::error_cant_build_this_here);
auto res = GameCommands::do_40(GameCommands::Flags::apply, *args);
if (res == GameCommands::FAILURE || res == 0)
{
return;
}
Audio::playSound(Audio::SoundId::construct, GameCommands::getPosition());
}
else
{
auto args = getTrackModsPlacementArgsFromCursor(x, y);
if (!args)
{
return;
}
if (args->trackObjType != _trackType)
{
Error::open(StringIds::error_cant_build_this_here, StringIds::wrong_type_of_track_road);
return;
}
GameCommands::setErrorTitle(StringIds::error_cant_build_this_here);
auto res = GameCommands::do_17(GameCommands::Flags::apply, *args);
if (res == GameCommands::FAILURE || res == 0)
{
return;
}
Audio::playSound(Audio::SoundId::construct, GameCommands::getPosition());
}
}
static void setCheckbox(Window* self, WidgetIndex_t checkboxIndex, string_id name)