Implement Signal toolUpdate/Down (#1093)
* Start implementing toolUpdate signal * Implement getSide and finish signal tool update * Implement signal toolDown * Actually name the game command * Use correct global address
This commit is contained in:
parent
99049e5fab
commit
80952a1c99
|
@ -59,8 +59,8 @@ namespace OpenLoco::GameCommands
|
|||
{ GameCommand::vehicleRename, Vehicles::rename, 0x004B6572, false },
|
||||
{ GameCommand::changeStationName, renameStation, 0x00490756, false },
|
||||
{ GameCommand::vehicleLocalExpress, nullptr, 0x004B694B, true },
|
||||
{ GameCommand::gc_unk_13, nullptr, 0x00488BDB, true },
|
||||
{ GameCommand::gc_unk_14, nullptr, 0x004891E4, true },
|
||||
{ GameCommand::createSignal, nullptr, 0x00488BDB, true },
|
||||
{ GameCommand::removeSignal, nullptr, 0x004891E4, true },
|
||||
{ GameCommand::gc_unk_15, nullptr, 0x0048BB20, true },
|
||||
{ GameCommand::removeTrackStation, nullptr, 0x0048C402, true },
|
||||
{ GameCommand::createTrackMod, nullptr, 0x004A6479, true },
|
||||
|
|
|
@ -46,8 +46,8 @@ namespace OpenLoco::GameCommands
|
|||
vehicleRename = 10,
|
||||
changeStationName = 11,
|
||||
vehicleLocalExpress = 12,
|
||||
gc_unk_13 = 13,
|
||||
gc_unk_14 = 14,
|
||||
createSignal = 13,
|
||||
removeSignal = 14,
|
||||
gc_unk_15 = 15,
|
||||
removeTrackStation = 16,
|
||||
createTrackMod = 17,
|
||||
|
@ -281,6 +281,49 @@ namespace OpenLoco::GameCommands
|
|||
doCommand(GameCommand::vehicleLocalExpress, regs);
|
||||
}
|
||||
|
||||
struct SignalPlacementArgs
|
||||
{
|
||||
SignalPlacementArgs() = default;
|
||||
explicit SignalPlacementArgs(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) & 0xFF)
|
||||
, trackObjType(regs.ebp & 0xFF)
|
||||
, sides((regs.edi >> 16) & 0xC000)
|
||||
{
|
||||
}
|
||||
|
||||
Map::Pos3 pos;
|
||||
uint8_t rotation;
|
||||
uint8_t trackId;
|
||||
uint8_t index;
|
||||
uint8_t type;
|
||||
uint8_t trackObjType;
|
||||
uint16_t sides;
|
||||
|
||||
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) | ((sides & 0xC000) << 16);
|
||||
regs.ebp = trackObjType;
|
||||
return regs;
|
||||
}
|
||||
};
|
||||
|
||||
inline uint32_t do_13(uint8_t flags, const SignalPlacementArgs& args)
|
||||
{
|
||||
registers regs = registers(args);
|
||||
regs.bl = flags;
|
||||
return doCommand(GameCommand::createSignal, regs);
|
||||
}
|
||||
|
||||
struct SignalRemovalArgs
|
||||
{
|
||||
SignalRemovalArgs() = default;
|
||||
|
@ -319,7 +362,7 @@ namespace OpenLoco::GameCommands
|
|||
{
|
||||
registers regs = registers(args);
|
||||
regs.bl = flags;
|
||||
return doCommand(GameCommand::gc_unk_14, regs) != FAILURE;
|
||||
return doCommand(GameCommand::removeSignal, regs) != FAILURE;
|
||||
}
|
||||
|
||||
struct TrackStationRemovalArgs
|
||||
|
|
|
@ -138,7 +138,8 @@ namespace OpenLoco::StringIds
|
|||
constexpr string_id tooltip_steep_slope_up = 136;
|
||||
constexpr string_id build_this = 137;
|
||||
constexpr string_id build_cost = 138;
|
||||
|
||||
constexpr string_id cant_build_signal_here = 139;
|
||||
constexpr string_id cant_build_signals_here = 140;
|
||||
constexpr string_id cant_remove_signal = 141;
|
||||
|
||||
constexpr string_id menu_underground_view = 145;
|
||||
|
|
|
@ -117,7 +117,7 @@ namespace OpenLoco::Ui
|
|||
/**
|
||||
* Maps a 2D viewport position to a UI (screen) position.
|
||||
*/
|
||||
Point mapToUi(const viewport_pos& vpos)
|
||||
Point mapToUi(const viewport_pos& vpos) const
|
||||
{
|
||||
auto uiX = x + ((vpos.x - view_x) >> zoom);
|
||||
auto uiY = y + ((vpos.y - view_y) >> zoom);
|
||||
|
@ -127,7 +127,7 @@ namespace OpenLoco::Ui
|
|||
/**
|
||||
* Maps a UI (screen) position to a 2D viewport position.
|
||||
*/
|
||||
viewport_pos uiToMap(const Point& pos)
|
||||
viewport_pos uiToMap(const Point& pos) const
|
||||
{
|
||||
int16_t viewport_x = ((pos.x - x) << zoom) + view_x;
|
||||
int16_t viewport_y = ((pos.y - y) << zoom) + view_y;
|
||||
|
|
|
@ -47,12 +47,19 @@ namespace OpenLoco::Ui::Windows::Construction
|
|||
static loco_global<uint16_t, 0x01135FBA> _word_1135FBA;
|
||||
static loco_global<uint16_t, 0x01135FBC> _word_1135FBC;
|
||||
static loco_global<uint16_t, 0x01135FBE> _word_1135FBE;
|
||||
static loco_global<Map::Pos3, 0x01135FC6> _1135FC6;
|
||||
static loco_global<uint16_t, 0x01135FCC> _1135FCC;
|
||||
static loco_global<Map::Pos3, 0x01135FCE> _1135FCE;
|
||||
static loco_global<uint16_t, 0x01135FD4> _word_1135FD4;
|
||||
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<uint16_t, 0x01136002> _signalGhostSides;
|
||||
static loco_global<Map::Pos3, 0x01136004> _signalGhostPos;
|
||||
static loco_global<uint16_t, 0x0113600A> _signalGhostTrackObjId;
|
||||
static loco_global<uint8_t, 0x01136010> _modGhostTrackObjId;
|
||||
static loco_global<uint8_t[17], 0x0113601D> _signalList;
|
||||
static loco_global<uint8_t, 0x0113602E> _lastSelectedSignal;
|
||||
|
@ -62,6 +69,9 @@ namespace OpenLoco::Ui::Windows::Construction
|
|||
static loco_global<uint8_t, 0x0113603A> _byte_113603A;
|
||||
static loco_global<uint8_t[17], 0x0113603B> _stationList;
|
||||
static loco_global<uint8_t, 0x0113604C> _lastSelectedStationType;
|
||||
static loco_global<uint8_t, 0x0113604D> _signalGhostRotation;
|
||||
static loco_global<uint8_t, 0x0113604E> _signalGhostTrackId;
|
||||
static loco_global<uint8_t, 0x0113604F> _signalGhostTileIndex;
|
||||
static loco_global<uint8_t[4], 0x01136054> _modList;
|
||||
static loco_global<uint8_t, 0x01136058> _modGhostRotation;
|
||||
static loco_global<uint8_t, 0x01136059> _modGhostTrackId;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#include "../../Audio/Audio.h"
|
||||
#include "../../GameCommands/GameCommands.h"
|
||||
#include "../../Graphics/ImageIds.h"
|
||||
#include "../../Input.h"
|
||||
#include "../../Localisation/FormatArguments.hpp"
|
||||
|
@ -5,6 +7,7 @@
|
|||
#include "../../Objects/ObjectManager.h"
|
||||
#include "../../Objects/TrackObject.h"
|
||||
#include "../../Objects/TrainSignalObject.h"
|
||||
#include "../../TrackData.h"
|
||||
#include "../../Ui/Dropdown.h"
|
||||
#include "../../Widget.h"
|
||||
#include "Construction.h"
|
||||
|
@ -114,26 +117,178 @@ namespace OpenLoco::Ui::Windows::Construction::Signal
|
|||
Common::onUpdate(self, (1 << 2));
|
||||
}
|
||||
|
||||
// Reverse direction map?
|
||||
static loco_global<uint8_t[16], 0x00503CAC> _503CAC;
|
||||
static loco_global<Map::Pos2[16], 0x00503C6C> _503C6C;
|
||||
|
||||
// 0x004A417A
|
||||
// false for left, true for right
|
||||
static bool getSide(const Map::Pos3& loc, const Point& mousePos, const TrackElement& elTrack, const Viewport& viewport)
|
||||
{
|
||||
// Get coordinates of first tile of track piece under the mouse
|
||||
const auto& piece = TrackData::getTrackPiece(elTrack.trackId())[elTrack.sequenceIndex()];
|
||||
const auto rotPos = Math::Vector::rotate(Map::Pos2(piece.x, piece.y), elTrack.unkDirection());
|
||||
const auto firstTile = loc - Map::Pos3(rotPos.x, rotPos.y, piece.z);
|
||||
|
||||
// Get coordinates of the next tile after the end of the track piece
|
||||
const auto trackAndDirection = (elTrack.trackId() << 3) | elTrack.unkDirection();
|
||||
const auto& trackSize = TrackData::getUnkTrack(trackAndDirection);
|
||||
const auto nextTile = firstTile + trackSize.pos;
|
||||
_1135FC6 = nextTile;
|
||||
_1135FCC = trackSize.rotationEnd;
|
||||
|
||||
// Get coordinates of the previous tile before the start of the track piece
|
||||
const auto unk = _503CAC[trackSize.rotationBegin];
|
||||
auto previousTile = firstTile;
|
||||
_word_1135FD4 = unk;
|
||||
if (unk < 12)
|
||||
{
|
||||
previousTile += _503C6C[unk];
|
||||
}
|
||||
_1135FCE = previousTile;
|
||||
|
||||
// Side is goverened by distance mouse is to either next or previous track coordinate
|
||||
const auto vpPosNext = gameToScreen(nextTile + Map::Pos3(16, 16, 0), viewport.getRotation());
|
||||
const auto uiPosNext = viewport.mapToUi(vpPosNext);
|
||||
const auto distanceToNext = Math::Vector::manhattanDistance(uiPosNext, mousePos);
|
||||
|
||||
const auto vpPosPrevious = gameToScreen(previousTile + Map::Pos3(16, 16, 0), viewport.getRotation());
|
||||
const auto uiPosPrevious = viewport.mapToUi(vpPosPrevious);
|
||||
const auto distanceToPrevious = Math::Vector::manhattanDistance(uiPosPrevious, mousePos);
|
||||
|
||||
return distanceToNext <= distanceToPrevious;
|
||||
}
|
||||
|
||||
static std::optional<GameCommands::SignalPlacementArgs> getSignalPlacementArgsFromCursor(const int16_t x, const int16_t y, const bool isBothDirectons)
|
||||
{
|
||||
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::SignalPlacementArgs args;
|
||||
args.type = _lastSelectedSignal;
|
||||
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();
|
||||
if (isBothDirectons)
|
||||
{
|
||||
args.sides = 0xC000;
|
||||
}
|
||||
else
|
||||
{
|
||||
args.sides = getSide(args.pos, { x, y }, *elTrack, *viewport) ? 0x8000 : 0x4000;
|
||||
}
|
||||
return { args };
|
||||
}
|
||||
|
||||
static uint32_t placeSignalGhost(const GameCommands::SignalPlacementArgs& args)
|
||||
{
|
||||
auto res = GameCommands::do_13(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 << 2);
|
||||
_signalGhostPos = args.pos;
|
||||
_signalGhostRotation = args.rotation;
|
||||
_signalGhostTrackId = args.trackId;
|
||||
_signalGhostTileIndex = args.index;
|
||||
_signalGhostSides = args.sides;
|
||||
_signalGhostTrackObjId = args.trackObjType;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// 0x0049E745
|
||||
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(0x0049E745, regs);
|
||||
if (widgetIndex != widx::single_direction && widgetIndex != widx::both_directions)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bool isBothDirections = widgetIndex == widx::both_directions;
|
||||
|
||||
auto placementArgs = getSignalPlacementArgsFromCursor(x, y, isBothDirections);
|
||||
if (!placementArgs || (placementArgs->trackObjType != _trackType))
|
||||
{
|
||||
removeConstructionGhosts();
|
||||
if (_signalCost != 0x80000000)
|
||||
{
|
||||
_signalCost = 0x80000000;
|
||||
self.invalidate();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_byte_522096 & (1 << 2))
|
||||
{
|
||||
if (*_signalGhostPos == placementArgs->pos
|
||||
&& _signalGhostRotation == placementArgs->rotation
|
||||
&& _signalGhostTrackId == placementArgs->trackId
|
||||
&& _signalGhostTileIndex == placementArgs->index
|
||||
&& _signalGhostSides == placementArgs->sides
|
||||
&& _signalGhostTrackObjId == placementArgs->trackObjType)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
removeConstructionGhosts();
|
||||
|
||||
auto cost = placeSignalGhost(*placementArgs);
|
||||
if (cost != _signalCost)
|
||||
{
|
||||
_signalCost = cost;
|
||||
self.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
// 0x0049E75A
|
||||
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(0x0049E75A, regs);
|
||||
if (widgetIndex != widx::single_direction && widgetIndex != widx::both_directions)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
removeConstructionGhosts();
|
||||
|
||||
const bool isBothDirections = widgetIndex == widx::both_directions;
|
||||
auto args = getSignalPlacementArgsFromCursor(x, y, isBothDirections);
|
||||
if (!args)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (args->trackObjType != _trackType)
|
||||
{
|
||||
Error::open(StringIds::cant_build_signal_here, StringIds::wrong_type_of_track_road);
|
||||
return;
|
||||
}
|
||||
|
||||
GameCommands::setErrorTitle(isBothDirections ? StringIds::cant_build_signals_here : StringIds::cant_build_signal_here);
|
||||
auto res = GameCommands::do_13(GameCommands::Flags::apply, *args);
|
||||
if (res == GameCommands::FAILURE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Audio::playSound(Audio::SoundId::construct, GameCommands::getPosition());
|
||||
}
|
||||
|
||||
// 0x0049E499
|
||||
|
|
Loading…
Reference in New Issue