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:
parent
b08815aeaf
commit
99049e5fab
|
@ -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 },
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue