Implement construction open At functions (#1094)

* Implement openAtTrack

* Split up functions for reuse

* Implement openAtRoad

* Minor refactorings

* Update changelog
This commit is contained in:
Duncan 2021-08-10 11:53:41 +01:00 committed by GitHub
parent 80952a1c99
commit 8461ad721b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 280 additions and 54 deletions

View File

@ -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.

View File

@ -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); }
};

View File

@ -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)
{

View File

@ -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

View File

@ -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;

View File

@ -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)