Implement construction open At functions (#1094)
* Implement openAtTrack * Split up functions for reuse * Implement openAtRoad * Minor refactorings * Update changelog
This commit is contained in:
parent
80952a1c99
commit
8461ad721b
|
@ -2,6 +2,7 @@
|
|||
------------------------------------------------------------------------
|
||||
- Fix: [#1035] Incorrect colour selection when building buildings.
|
||||
- Fix: [#1070] Crash when naming stations after exhausting natural names.
|
||||
- Fix: [#1094] Repeated clicking on construction window not always working.
|
||||
- Change: [#298] Planting clusters of trees now costs money and influences ratings outside of editor mode.
|
||||
- Change: [#1079] Allow rotating buildings in town list by keyboard shortcut.
|
||||
|
||||
|
|
|
@ -258,10 +258,11 @@ namespace OpenLoco::Map
|
|||
bool has_4_80() const { return (_4 & 0x80) != 0; }
|
||||
uint8_t trackObjectId() const { return _5 >> 4; } // _5u
|
||||
uint8_t sequenceIndex() const { return _5 & 0xF; } // _5l
|
||||
uint8_t unk_6() const { return _6; } // _6
|
||||
uint8_t bridge() const { return _6 >> 5; } // _6u
|
||||
uint8_t owner() const { return _7 & 0xF; } // _7l
|
||||
void setOwner(uint8_t newOwner) { _7 = (_7 & 0xF0) | (newOwner & 0xF); }
|
||||
bool hasMod(uint8_t mod) const { return _7 & (1 << (4 + mod)); } // _7u
|
||||
uint8_t mods() const { return _7 >> 4; } // _7u
|
||||
};
|
||||
|
||||
struct SignalElement : public TileElementBase
|
||||
|
@ -289,11 +290,14 @@ namespace OpenLoco::Map
|
|||
|
||||
public:
|
||||
uint8_t unkDirection() const { return _type & 0x03; }
|
||||
uint8_t roadId() const { return _4 & 0xF; } // _4l
|
||||
uint8_t roadId() const { return _4 & 0xF; } // _4l
|
||||
bool has_4_80() const { return (_4 & 0x80) != 0; }
|
||||
uint8_t roadObjectId() const { return _5 >> 4; } // _5u
|
||||
uint8_t sequenceIndex() const { return _5 & 0x3; } // _5l
|
||||
uint8_t bridge() const { return _6 >> 5; } // _6u
|
||||
bool hasStationElement() const { return (_type & 0x80) != 0; }
|
||||
bool hasMod(uint8_t mod) const { return _7 & (1 << (mod + 6)); } // _7u (bits 6 and 7)
|
||||
uint8_t mods() const { return _7 >> 6; } // _7u
|
||||
uint8_t owner() const { return _7 & 0xF; } // _7l
|
||||
void setOwner(uint8_t newOwner) { _7 = (_7 & 0xF0) | (newOwner & 0xF); }
|
||||
};
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "../../Objects/TrainSignalObject.h"
|
||||
#include "../../Objects/TrainStationObject.h"
|
||||
#include "../../StationManager.h"
|
||||
#include "../../TrackData.h"
|
||||
#include "../../Widget.h"
|
||||
#include "Construction.h"
|
||||
|
||||
|
@ -203,27 +204,185 @@ namespace OpenLoco::Ui::Windows::Construction
|
|||
// 0x004A0EAD
|
||||
Window* openAtTrack(Window* main, TrackElement* track, const Pos2 pos)
|
||||
{
|
||||
registers regs{};
|
||||
regs.esi = X86Pointer(main);
|
||||
regs.edx = X86Pointer(track);
|
||||
regs.ax = pos.x;
|
||||
regs.cx = pos.y;
|
||||
call(0x004A0EAD, regs);
|
||||
auto* viewport = main->viewports[0];
|
||||
_backupTileElement = *reinterpret_cast<TileElement*>(track);
|
||||
auto* copyElement = (*_backupTileElement).asTrack();
|
||||
if (copyElement == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return reinterpret_cast<Window*>(regs.esi);
|
||||
if (copyElement->owner() != CompanyManager::getControllingId())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
removeConstructionGhosts();
|
||||
auto* wnd = WindowManager::find(WindowType::construction);
|
||||
if (wnd == nullptr)
|
||||
{
|
||||
WindowManager::closeConstructionWindows();
|
||||
Common::createConstructionWindow();
|
||||
}
|
||||
else
|
||||
{
|
||||
Common::resetWindow(*wnd, Common::widx::tab_construction);
|
||||
}
|
||||
|
||||
_trackType = copyElement->trackObjectId();
|
||||
_byte_1136063 = 0;
|
||||
Common::setTrackOptions(_trackType);
|
||||
|
||||
_constructionHover = 0;
|
||||
_byte_113607E = 1;
|
||||
_trackCost = 0x80000000;
|
||||
_byte_1136076 = 0;
|
||||
_lastSelectedTrackModSection = 0;
|
||||
|
||||
Common::setNextAndPreviousTrackTile(*copyElement, pos);
|
||||
|
||||
const bool isCloserToNext = Common::isPointCloserToNextOrPreviousTile(Input::getDragLastLocation(), *viewport);
|
||||
|
||||
const auto chosenLoc = isCloserToNext ? *_nextTile : *_previousTile;
|
||||
const auto chosenRotation = isCloserToNext ? _nextTileRotation : _previousTileRotation;
|
||||
_x = chosenLoc.x;
|
||||
_y = chosenLoc.y;
|
||||
_constructionZ = chosenLoc.z;
|
||||
_constructionRotation = chosenRotation;
|
||||
_lastSelectedTrackPiece = 0;
|
||||
_lastSelectedTrackGradient = 0;
|
||||
|
||||
Common::refreshSignalList(_signalList, _trackType);
|
||||
auto lastSignal = _scenarioSignals[_trackType];
|
||||
|
||||
if (lastSignal == 0xFF)
|
||||
lastSignal = _signalList[0];
|
||||
|
||||
_lastSelectedSignal = lastSignal;
|
||||
|
||||
Common::refreshStationList(_stationList, _trackType, TransportMode::rail);
|
||||
|
||||
auto lastStation = _scenarioTrainStations[_trackType];
|
||||
|
||||
if (lastStation == 0xFF)
|
||||
lastStation = _stationList[0];
|
||||
|
||||
_lastSelectedStationType = lastStation;
|
||||
|
||||
Common::refreshBridgeList(_bridgeList, _trackType, TransportMode::rail);
|
||||
|
||||
auto lastBridge = _scenarioBridges[_trackType];
|
||||
|
||||
if (lastBridge == 0xFF)
|
||||
lastBridge = _bridgeList[0];
|
||||
|
||||
_lastSelectedBridge = lastBridge;
|
||||
|
||||
if (copyElement->has_4_80())
|
||||
{
|
||||
_lastSelectedBridge = copyElement->bridge();
|
||||
}
|
||||
Common::refreshModList(_modList, _trackType, TransportMode::rail);
|
||||
|
||||
_lastSelectedMods = copyElement->mods();
|
||||
_byte_113603A = 0;
|
||||
auto* window = WindowManager::find(WindowType::construction);
|
||||
|
||||
if (window != nullptr)
|
||||
{
|
||||
Common::setDisabledWidgets(window);
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
// 0x004A147F
|
||||
Window* openAtRoad(Window* main, RoadElement* track, const Pos2 pos)
|
||||
Window* openAtRoad(Window* main, RoadElement* road, const Pos2 pos)
|
||||
{
|
||||
registers regs{};
|
||||
regs.esi = X86Pointer(main);
|
||||
regs.edx = X86Pointer(track);
|
||||
regs.ax = pos.x;
|
||||
regs.cx = pos.y;
|
||||
call(0x004A147F, regs);
|
||||
auto* viewport = main->viewports[0];
|
||||
_backupTileElement = *reinterpret_cast<TileElement*>(road);
|
||||
auto* copyElement = (*_backupTileElement).asRoad();
|
||||
if (copyElement == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return reinterpret_cast<Window*>(regs.esi);
|
||||
removeConstructionGhosts();
|
||||
auto* wnd = WindowManager::find(WindowType::construction);
|
||||
if (wnd == nullptr)
|
||||
{
|
||||
WindowManager::closeConstructionWindows();
|
||||
Common::createConstructionWindow();
|
||||
}
|
||||
else
|
||||
{
|
||||
Common::resetWindow(*wnd, Common::widx::tab_construction);
|
||||
}
|
||||
|
||||
_trackType = copyElement->roadObjectId() | (1 << 7);
|
||||
_byte_1136063 = 0;
|
||||
Common::setTrackOptions(_trackType);
|
||||
|
||||
_constructionHover = 0;
|
||||
_byte_113607E = 1;
|
||||
_trackCost = 0x80000000;
|
||||
_byte_1136076 = 0;
|
||||
_lastSelectedTrackModSection = 0;
|
||||
|
||||
Common::setNextAndPreviousRoadTile(*copyElement, pos);
|
||||
|
||||
const bool isCloserToNext = Common::isPointCloserToNextOrPreviousTile(Input::getDragLastLocation(), *viewport);
|
||||
|
||||
const auto chosenLoc = isCloserToNext ? *_nextTile : *_previousTile;
|
||||
const auto chosenRotation = isCloserToNext ? _nextTileRotation : _previousTileRotation;
|
||||
_x = chosenLoc.x;
|
||||
_y = chosenLoc.y;
|
||||
_constructionZ = chosenLoc.z;
|
||||
_constructionRotation = chosenRotation;
|
||||
_lastSelectedTrackPiece = 0;
|
||||
_lastSelectedTrackGradient = 0;
|
||||
_lastSelectedSignal = 0xFF;
|
||||
|
||||
Common::refreshStationList(_stationList, _trackType, TransportMode::road);
|
||||
|
||||
auto lastStation = _scenarioRoadStations[(_trackType & ~(1ULL << 7))];
|
||||
|
||||
if (lastStation == 0xFF)
|
||||
lastStation = _stationList[0];
|
||||
|
||||
_lastSelectedStationType = lastStation;
|
||||
|
||||
Common::refreshBridgeList(_bridgeList, _trackType, TransportMode::road);
|
||||
|
||||
auto lastBridge = _scenarioBridges[(_trackType & ~(1ULL << 7))];
|
||||
|
||||
if (lastBridge == 0xFF)
|
||||
lastBridge = _bridgeList[0];
|
||||
|
||||
_lastSelectedBridge = lastBridge;
|
||||
if (copyElement->has_4_80())
|
||||
{
|
||||
_lastSelectedBridge = copyElement->bridge();
|
||||
}
|
||||
|
||||
Common::refreshModList(_modList, _trackType, TransportMode::road);
|
||||
|
||||
_lastSelectedMods = 0;
|
||||
auto* roadObj = ObjectManager::get<RoadObject>(_trackType & ~(1ULL << 7));
|
||||
if (roadObj->flags & Flags12::unk_03)
|
||||
{
|
||||
_lastSelectedMods = copyElement->mods();
|
||||
}
|
||||
_byte_113603A = 0;
|
||||
|
||||
auto* window = WindowManager::find(WindowType::construction);
|
||||
|
||||
if (window != nullptr)
|
||||
{
|
||||
Common::setDisabledWidgets(window);
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
// 0x004A1303
|
||||
|
@ -307,7 +466,7 @@ namespace OpenLoco::Ui::Windows::Construction
|
|||
_byte_1136063 = flags >> 24;
|
||||
_x = 0x1800;
|
||||
_y = 0x1800;
|
||||
_word_1135FB8 = 0x100;
|
||||
_constructionZ = 0x100;
|
||||
_constructionRotation = 0;
|
||||
_constructionHover = 0;
|
||||
_byte_113607E = 1;
|
||||
|
@ -426,6 +585,90 @@ namespace OpenLoco::Ui::Windows::Construction
|
|||
self->activated_widgets |= (1ULL << Common::tabInformationByTabOffset[self->current_tab].widgetIndex);
|
||||
}
|
||||
|
||||
// 0x004A0EF4
|
||||
void resetWindow(Window& self, WidgetIndex_t tabWidgetIndex)
|
||||
{
|
||||
self.current_tab = tabWidgetIndex - widx::tab_construction;
|
||||
|
||||
const auto& tabInfo = tabInformationByTabOffset[tabWidgetIndex - widx::tab_construction];
|
||||
|
||||
self.enabled_widgets = tabInfo.enabledWidgets;
|
||||
self.event_handlers = tabInfo.events;
|
||||
self.activated_widgets = 0;
|
||||
self.widgets = tabInfo.widgets;
|
||||
|
||||
setDisabledWidgets(&self);
|
||||
|
||||
self.width = self.widgets[widx::frame].right + 1;
|
||||
self.height = self.widgets[widx::frame].bottom + 1;
|
||||
}
|
||||
|
||||
// Reverse direction map?
|
||||
static loco_global<uint8_t[16], 0x00503CAC> _503CAC;
|
||||
static loco_global<Map::Pos2[16], 0x00503C6C> _503C6C;
|
||||
|
||||
void setNextAndPreviousTrackTile(const TrackElement& elTrack, const Map::Pos2& pos)
|
||||
{
|
||||
const auto& piece = TrackData::getTrackPiece(elTrack.trackId())[elTrack.sequenceIndex()];
|
||||
const auto firstTileOffset = Math::Vector::rotate(Map::Pos2(piece.x, piece.y), elTrack.unkDirection());
|
||||
const auto firstTile = Map::Pos3(pos.x, pos.y, elTrack.baseZ() * 4) - Map::Pos3(firstTileOffset.x, firstTileOffset.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;
|
||||
_nextTile = nextTile;
|
||||
_nextTileRotation = trackSize.rotationEnd;
|
||||
|
||||
// Get coordinates of the previous tile before the start of the track piece
|
||||
const auto unk = _503CAC[trackSize.rotationBegin];
|
||||
auto previousTile = firstTile;
|
||||
_previousTileRotation = unk;
|
||||
if (unk < 12)
|
||||
{
|
||||
previousTile += _503C6C[unk];
|
||||
}
|
||||
_previousTile = previousTile;
|
||||
}
|
||||
|
||||
void setNextAndPreviousRoadTile(const RoadElement& elRoad, const Map::Pos2& pos)
|
||||
{
|
||||
const auto& piece = TrackData::getTrackPiece(elRoad.roadId())[elRoad.sequenceIndex()];
|
||||
const auto firstTileOffset = Math::Vector::rotate(Map::Pos2(piece.x, piece.y), elRoad.unkDirection());
|
||||
const auto firstTile = Map::Pos3(pos.x, pos.y, elRoad.baseZ() * 4) - Map::Pos3(firstTileOffset.x, firstTileOffset.y, piece.z);
|
||||
|
||||
// Get coordinates of the next tile after the end of the track piece
|
||||
const auto trackAndDirection = (elRoad.roadId() << 3) | elRoad.unkDirection();
|
||||
const auto& trackSize = TrackData::getUnkRoad(trackAndDirection);
|
||||
const auto nextTile = firstTile + trackSize.pos;
|
||||
_nextTile = nextTile;
|
||||
_nextTileRotation = trackSize.rotationEnd;
|
||||
|
||||
// Get coordinates of the previous tile before the start of the track piece
|
||||
const auto unk = _503CAC[trackSize.rotationBegin];
|
||||
auto previousTile = firstTile;
|
||||
_previousTileRotation = unk;
|
||||
if (unk < 12)
|
||||
{
|
||||
previousTile += _503C6C[unk];
|
||||
}
|
||||
_previousTile = previousTile;
|
||||
}
|
||||
|
||||
// True for next, false for previous
|
||||
bool isPointCloserToNextOrPreviousTile(const Point& point, const Viewport& viewport)
|
||||
{
|
||||
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, Input::getDragLastLocation());
|
||||
|
||||
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, Input::getDragLastLocation());
|
||||
|
||||
return distanceToNext < distanceToPrevious;
|
||||
}
|
||||
|
||||
// 0x0049D93A
|
||||
void switchTab(Window* self, WidgetIndex_t widgetIndex)
|
||||
{
|
||||
|
|
|
@ -43,14 +43,14 @@ namespace OpenLoco::Ui::Windows::Construction
|
|||
static loco_global<uint16_t, 0x01135F86> _word_1135F86;
|
||||
static loco_global<uint16_t, 0x01135FB4> _x;
|
||||
static loco_global<uint16_t, 0x01135FB6> _y;
|
||||
static loco_global<uint16_t, 0x01135FB8> _word_1135FB8;
|
||||
static loco_global<uint16_t, 0x01135FB8> _constructionZ;
|
||||
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<Map::Pos3, 0x01135FC6> _nextTile;
|
||||
static loco_global<uint16_t, 0x01135FCC> _nextTileRotation;
|
||||
static loco_global<Map::Pos3, 0x01135FCE> _previousTile;
|
||||
static loco_global<uint16_t, 0x01135FD4> _previousTileRotation;
|
||||
static loco_global<uint16_t, 0x01135FD6> _word_1135FD6;
|
||||
static loco_global<uint16_t, 0x01135FD8> _word_1135FD8;
|
||||
static loco_global<uint16_t, 0x01135FE4> _lastSelectedMods;
|
||||
|
@ -93,6 +93,7 @@ namespace OpenLoco::Ui::Windows::Construction
|
|||
static loco_global<uint8_t, 0x01136078> _byte_1136078;
|
||||
static loco_global<uint8_t, 0x01136079> _lastSelectedTrackPieceId;
|
||||
static loco_global<uint8_t, 0x0113607E> _byte_113607E;
|
||||
static loco_global<Map::TileElement, 0x01136090> _backupTileElement;
|
||||
|
||||
namespace Common
|
||||
{
|
||||
|
@ -121,6 +122,7 @@ namespace OpenLoco::Ui::Windows::Construction
|
|||
constexpr uint64_t enabledWidgets = (1 << widx::caption) | (1 << widx::close_button) | (1 << widx::tab_construction) | (1 << widx::tab_station) | (1 << widx::tab_signal) | (1 << widx::tab_overhead);
|
||||
|
||||
void prepareDraw(Window* self);
|
||||
void resetWindow(Window& self, WidgetIndex_t tabWidgetIndex);
|
||||
void switchTab(Window* self, WidgetIndex_t widgetIndex);
|
||||
void repositionTabs(Window* self);
|
||||
void drawTabs(Window* self, Gfx::Context* context);
|
||||
|
@ -138,6 +140,9 @@ namespace OpenLoco::Ui::Windows::Construction
|
|||
void refreshModList(uint8_t* modList, uint8_t trackType, TransportMode transportMode);
|
||||
void sub_4A3A50();
|
||||
void refreshSignalList(uint8_t* signalList, uint8_t trackType);
|
||||
void setNextAndPreviousTrackTile(const TrackElement& elTrack, const Map::Pos2& pos);
|
||||
void setNextAndPreviousRoadTile(const RoadElement& elRoad, const Map::Pos2& pos);
|
||||
bool isPointCloserToNextOrPreviousTile(const Point& point, const Viewport& viewport);
|
||||
}
|
||||
|
||||
namespace Construction
|
||||
|
|
|
@ -1503,7 +1503,7 @@ namespace OpenLoco::Ui::Windows::Construction::Construction
|
|||
_byte_113607E = 0;
|
||||
_x = mapPos.x;
|
||||
_y = mapPos.y;
|
||||
_word_1135FB8 = height;
|
||||
_constructionZ = height;
|
||||
_byte_522096 = 0;
|
||||
_byte_1136066 = 0;
|
||||
|
||||
|
|
|
@ -125,38 +125,11 @@ namespace OpenLoco::Ui::Windows::Construction::Signal
|
|||
// 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);
|
||||
Common::setNextAndPreviousTrackTile(elTrack, loc);
|
||||
|
||||
// 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;
|
||||
const bool isCloserToNext = Common::isPointCloserToNextOrPreviousTile(mousePos, viewport);
|
||||
|
||||
// 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;
|
||||
return isCloserToNext;
|
||||
}
|
||||
|
||||
static std::optional<GameCommands::SignalPlacementArgs> getSignalPlacementArgsFromCursor(const int16_t x, const int16_t y, const bool isBothDirectons)
|
||||
|
|
Loading…
Reference in New Issue