Construction Window ToolUpdate (#1119)

* Start Implementing track tool update

* Further work

* Finish function

* Fix bugs

* Fix view issue

* Extract out common function

* Implement road tool update
This commit is contained in:
Duncan 2021-08-26 21:42:41 +01:00 committed by GitHub
parent 070ec04888
commit a01bd32132
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 352 additions and 69 deletions

View File

@ -269,7 +269,7 @@ namespace OpenLoco::GameCommands
registers regs;
regs.eax = pos.x;
regs.cx = pos.y;
regs.edi = pos.z | (mods << 16) | (unk ? 0x800000 : 0);
regs.edi = (0xFFFF & pos.z) | (mods << 16) | (unk ? 0x800000 : 0);
regs.bh = rotation;
regs.edx = trackObjectId | (trackId << 8) | (bridge << 24);
return regs;

View File

@ -361,6 +361,7 @@ namespace OpenLoco::StringIds
constexpr string_id prompt_enter_new_vehicle_name = 376;
constexpr string_id cant_rename_this_vehicle = 377;
constexpr string_id bridge_type_unsuitable_for_this_configuration = 382;
constexpr string_id title_station_name = 383;
constexpr string_id prompt_type_new_station_name = 384;
constexpr string_id error_cant_rename_station = 385;

View File

@ -44,9 +44,11 @@ namespace OpenLoco::Ui::Windows::Construction
static loco_global<uint16_t, 0x01135FB4> _x;
static loco_global<uint16_t, 0x01135FB6> _y;
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, 0x01135FBA> _ghostTrackPos;
static loco_global<Map::Pos3, 0x01135FC0> _ghostRemovalTrackPos;
static loco_global<uint8_t, 0x0113606A> _ghostRemovalTrackId;
static loco_global<uint8_t, 0x01136069> _ghostRemovalTrackRotation;
static loco_global<uint8_t, 0x00522093> _ghostRemovalTrackObjectId;
static loco_global<Map::Pos3, 0x01135FC6> _nextTile;
static loco_global<uint16_t, 0x01135FCC> _nextTileRotation;
static loco_global<Map::Pos3, 0x01135FCE> _previousTile;
@ -86,6 +88,7 @@ namespace OpenLoco::Ui::Windows::Construction
static loco_global<uint8_t, 0x01136067> _lastSelectedTrackPiece;
static loco_global<uint8_t, 0x01136068> _lastSelectedTrackGradient;
static loco_global<uint8_t, 0x0113606E> _lastSelectedTrackModSection;
static loco_global<uint8_t, 0x01136072> _byte_1136072;
static loco_global<uint8_t, 0x01136073> _byte_1136073;
static loco_global<uint8_t, 0x01136075> _byte_1136075;
static loco_global<uint8_t, 0x01136076> _byte_1136076;

View File

@ -29,6 +29,9 @@ namespace OpenLoco::Ui::Windows::Construction::Construction
static loco_global<uint16_t, 0x00523376> _clickRepeatTicks;
static loco_global<uint32_t, 0x00523394> _toolWidgetIndex;
static loco_global<Map::Pos3, 0x00F24942> _constructionArrowPos;
static loco_global<uint8_t, 0x00F24948> _constructionArrowDirection;
static loco_global<int32_t, 0x00E3F0B8> gCurrentRotation;
static loco_global<uint8_t, 0x0112C2E9> _alternateTrackObjectId; // set from GameCommands::createRoad
static loco_global<uint8_t[18], 0x0050A006> available_objects; // toptoolbar
@ -937,6 +940,22 @@ namespace OpenLoco::Ui::Windows::Construction::Construction
disableUnusedTrackPieces(self, trackObj, disabledWidgets);
}
static void setMapSelectedTilesFromPiece(const std::vector<TrackData::PreviewTrack>& pieces, const Map::Pos2& origin, const uint8_t rotation)
{
size_t i = 0;
for (const auto& piece : pieces)
{
if (piece.flags & Map::TrackData::PreviewTrackFlags::diagonal)
{
continue;
}
_mapSelectedTiles[i++] = origin + Math::Vector::rotate(Map::Pos2{ piece.x, piece.y }, rotation);
}
_mapSelectedTiles[i].x = -1;
mapInvalidateMapSelectionTiles();
}
static void activateSelectedRoadWidgets(Window* window)
{
TileManager::mapInvalidateMapSelectionTiles();
@ -962,28 +981,9 @@ namespace OpenLoco::Ui::Windows::Construction::Construction
}
const auto& roadPiece = Map::TrackData::getRoadPiece(roadId);
auto posId = 0;
rotation &= 3;
for (const auto& roadPart : roadPiece)
{
if (roadPart.flags & Map::TrackData::PreviewTrackFlags::diagonal)
{
continue;
}
Pos2 pos = { roadPart.x, roadPart.y };
pos = Math::Vector::rotate(pos, rotation);
pos.x += x;
pos.y += y;
_mapSelectedTiles[posId] = pos;
posId++;
}
_mapSelectedTiles[posId].x = -1;
mapInvalidateMapSelectionTiles();
setMapSelectedTilesFromPiece(roadPiece, Map::Pos2(x, y), rotation);
window->holdable_widgets = (1 << widx::construct) | (1 << widx::remove);
auto trackType = _trackType & ~(1 << 7);
@ -1156,27 +1156,9 @@ namespace OpenLoco::Ui::Windows::Construction::Construction
}
const auto& trackPiece = Map::TrackData::getTrackPiece(trackId);
auto posId = 0;
rotation &= 3;
for (const auto& trackPart : trackPiece)
{
if (trackPart.flags & Map::TrackData::PreviewTrackFlags::diagonal)
{
continue;
}
Pos2 pos = { trackPart.x, trackPart.y };
pos = Math::Vector::rotate(pos, rotation);
pos.x += x;
pos.y += y;
_mapSelectedTiles[posId] = pos;
posId++;
}
_mapSelectedTiles[posId].x = -1;
mapInvalidateMapSelectionTiles();
setMapSelectedTilesFromPiece(trackPiece, Map::Pos2(x, y), rotation);
window->holdable_widgets = (1 << widx::construct) | (1 << widx::remove);
auto trackObj = ObjectManager::get<TrackObject>(_trackType);
@ -2218,28 +2200,6 @@ namespace OpenLoco::Ui::Windows::Construction::Construction
}
}
// 0x0049DC8C
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(0x0049DC8C, regs);
}
static int16_t getMaxPieceHeight(const std::vector<TrackData::PreviewTrack>& piece)
{
int16_t maxPieceHeight = 0;
for (const auto& part : piece)
{
maxPieceHeight = std::max(maxPieceHeight, part.z);
}
return maxPieceHeight;
}
// 0 if nothing currently selected
static int16_t getMaxConstructHeightFromExistingSelection()
{
@ -2247,13 +2207,16 @@ namespace OpenLoco::Ui::Windows::Construction::Construction
if (Input::hasMapSelectionFlag(Input::MapSelectionFlags::enableConstruct))
{
auto i = 0;
for (auto& tile = _mapSelectedTiles[i]; tile.x != -1; tile = _mapSelectedTiles[++i])
for (const auto& tile : _mapSelectedTiles)
{
if (tile.x == -1)
{
break;
}
if (!Map::validCoords(tile))
continue;
const auto tileHeight = getConstructionHeight(_mapSelectedTiles[i]);
const auto tileHeight = getConstructionHeight(tile);
if (!tileHeight)
{
continue;
@ -2306,7 +2269,7 @@ namespace OpenLoco::Ui::Windows::Construction::Construction
return std::nullopt;
}
static std::optional<std::pair<Map::TilePos2, int16_t>> getConstructionPos(const int16_t x, const int16_t y, const int16_t baseHeight)
static std::optional<std::pair<Map::TilePos2, int16_t>> getConstructionPos(const int16_t x, const int16_t y, const int16_t baseHeight = std::numeric_limits<int16_t>::max())
{
auto mapPos = ViewportInteraction::getSurfaceOrWaterLocFromUi({ x, y });
@ -2324,6 +2287,322 @@ namespace OpenLoco::Ui::Windows::Construction::Construction
return { std::make_pair(Map::TilePos2(*mapPos), height) };
}
static int16_t getMaxPieceHeight(const std::vector<TrackData::PreviewTrack>& piece)
{
int16_t maxPieceHeight = 0;
for (const auto& part : piece)
{
maxPieceHeight = std::max(maxPieceHeight, part.z);
}
return maxPieceHeight;
}
// 0x004A193B
static void sub_4A193B()
{
for (const auto bridgeType : _bridgeList)
{
if (bridgeType == 0xFF)
{
return;
}
if (*_byte_1136075 == bridgeType)
{
_lastSelectedBridge = bridgeType;
return;
}
}
}
static void removeTrackGhosts()
{
call(0x004A006C);
}
static loco_global<uint16_t[44], 0x004F8764> _4F8764;
static loco_global<uint16_t[10], 0x004F8764> _4F7284;
// 0x0049FB63
static uint32_t placeTrackGhost(const GameCommands::TrackPlacementArgs& args)
{
removeTrackGhosts();
const auto res = GameCommands::doCommand(args, GameCommands::Flags::apply | GameCommands::Flags::flag_1 | GameCommands::Flags::flag_3 | GameCommands::Flags::flag_5 | GameCommands::Flags::flag_6);
if (res == GameCommands::FAILURE)
{
if (GameCommands::getErrorText() == StringIds::bridge_type_unsuitable_for_this_configuration)
{
_byte_113603A = 0;
for (const auto bridgeType : _bridgeList)
{
if (bridgeType == 0xFF)
{
break;
}
const auto* bridgeObj = ObjectManager::get<BridgeObject>(bridgeType);
if (bridgeObj->disabled_track_cfg & _4F8764[args.trackId])
{
continue;
}
if (bridgeType == _lastSelectedBridge)
{
break;
}
auto newArgs(args);
newArgs.bridge = bridgeType;
_lastSelectedBridge = bridgeType;
WindowManager::invalidate(WindowType::construction);
return placeTrackGhost(args);
}
}
}
else
{
_ghostRemovalTrackPos = args.pos;
_ghostRemovalTrackId = args.trackId;
_ghostRemovalTrackObjectId = args.trackObjectId;
_ghostRemovalTrackRotation = args.rotation;
_byte_522096 = (1 << 1) | *_byte_522096;
const auto newViewState = (_byte_1136072 & (1 << 1)) ? WindowManager::ViewportVisibility::undergroundView : WindowManager::ViewportVisibility::overgroundView;
WindowManager::viewportSetVisibility(newViewState);
if (_lastSelectedTrackGradient != 0)
{
WindowManager::viewportSetVisibility(WindowManager::ViewportVisibility::heightMarksOnLand);
}
}
_byte_113603A = 0;
return res;
}
// 0x0049FC60
static uint32_t placeRoadGhost(const GameCommands::RoadPlacementArgs& args)
{
removeTrackGhosts();
const auto res = GameCommands::doCommand(args, GameCommands::Flags::apply | GameCommands::Flags::flag_1 | GameCommands::Flags::flag_3 | GameCommands::Flags::flag_5 | GameCommands::Flags::flag_6);
if (res == GameCommands::FAILURE)
{
if (GameCommands::getErrorText() == StringIds::bridge_type_unsuitable_for_this_configuration)
{
_byte_113603A = 0;
for (const auto bridgeType : _bridgeList)
{
if (bridgeType == 0xFF)
{
break;
}
const auto* bridgeObj = ObjectManager::get<BridgeObject>(bridgeType);
if (bridgeObj->disabled_track_cfg & _4F7284[args.roadId])
{
continue;
}
if (bridgeType == _lastSelectedBridge)
{
break;
}
auto newArgs(args);
newArgs.bridge = bridgeType;
_lastSelectedBridge = bridgeType;
WindowManager::invalidate(WindowType::construction);
return placeRoadGhost(args);
}
}
}
else
{
_ghostRemovalTrackPos = args.pos;
_ghostRemovalTrackId = args.roadId;
_ghostRemovalTrackObjectId = args.roadObjectId | (1 << 7);
_ghostRemovalTrackRotation = args.rotation;
_byte_522096 = (1 << 1) | *_byte_522096;
const auto newViewState = (_byte_1136072 & (1 << 1)) ? WindowManager::ViewportVisibility::undergroundView : WindowManager::ViewportVisibility::overgroundView;
WindowManager::viewportSetVisibility(newViewState);
if (_lastSelectedTrackGradient != 0)
{
WindowManager::viewportSetVisibility(WindowManager::ViewportVisibility::heightMarksOnLand);
}
}
_byte_113603A = 0;
return res;
}
static std::optional<GameCommands::TrackPlacementArgs> getTrackPlacementArgs(const Map::Pos3& pos, const uint8_t trackPiece, const uint8_t gradient, const uint8_t rotation)
{
auto trackId = getTrackPieceId(trackPiece, gradient, rotation);
if (!trackId)
{
return std::nullopt;
}
GameCommands::TrackPlacementArgs args;
args.pos = pos;
args.bridge = _lastSelectedBridge;
args.mods = _lastSelectedMods;
args.rotation = trackId->rotation;
args.trackObjectId = _trackType;
args.trackId = trackId->id;
args.unk = _byte_113607E & (1 << 0);
return args;
}
static std::optional<GameCommands::RoadPlacementArgs> getRoadPlacementArgs(const Map::Pos3& pos, const uint8_t trackPiece, const uint8_t gradient, const uint8_t rotation)
{
auto roadId = getRoadPieceId(trackPiece, gradient, rotation);
if (!roadId)
{
return std::nullopt;
}
GameCommands::RoadPlacementArgs args;
args.pos = pos;
args.bridge = _lastSelectedBridge;
args.mods = _lastSelectedMods;
args.rotation = roadId->rotation;
args.roadObjectId = _trackType & ~(1 << 7);
args.roadId = roadId->id;
return args;
}
template<typename GetPlacementArgsFunc, typename PlaceGhostFunc>
static void constructionGhostLoop(const Pos3& mapPos, uint32_t maxRetries, GetPlacementArgsFunc&& getPlacementArgs, PlaceGhostFunc&& placeGhost)
{
_x = mapPos.x;
_y = mapPos.y;
_constructionZ = mapPos.z;
if (_byte_522096 & (1 << 1))
{
if (*_ghostTrackPos == mapPos)
{
return;
}
}
_ghostTrackPos = mapPos;
auto args = getPlacementArgs(mapPos, _lastSelectedTrackPiece, _lastSelectedTrackGradient, _constructionRotation);
if (!args)
{
return;
}
while (true)
{
auto res = placeGhost(*args);
_trackCost = res;
_byte_1136076 = _byte_1136073;
sub_4A193B();
if (_trackCost == 0x80000000)
{
maxRetries--;
if (maxRetries != 0)
{
args->pos.z -= 16;
if (args->pos.z >= 0)
{
if (Input::hasKeyModifier(Input::KeyModifier::shift))
{
continue;
}
else
{
args->pos.z += 32;
continue;
}
}
}
}
activateSelectedConstructionWidgets();
return;
}
}
// 0x004A1968
template<typename TGetPieceId, typename TTryMakeJunction, typename TGetPiece, typename GetPlacementArgsFunc, typename PlaceGhostFunc>
static void onToolUpdateTrack(const int16_t x, const int16_t y, TGetPieceId&& getPieceId, TTryMakeJunction&& tryMakeJunction, TGetPiece&& getPiece, GetPlacementArgsFunc&& getPlacementArgs, PlaceGhostFunc&& placeGhost)
{
Map::TileManager::mapInvalidateMapSelectionTiles();
Input::resetMapSelectionFlag(Input::MapSelectionFlags::enable | Input::MapSelectionFlags::enableConstruct | Input::MapSelectionFlags::unk_02);
Pos2 constructPos;
int16_t constructHeight = 0;
const auto junctionRes = tryMakeJunction(x, y);
if (junctionRes)
{
constructPos = junctionRes->first;
constructHeight = junctionRes->second;
_makeJunction = 1;
}
else
{
const auto constRes = getConstructionPos(x, y);
if (!constRes)
{
return;
}
constructPos = constRes->first;
constructHeight = constRes->second;
_makeJunction = 0;
}
Input::setMapSelectionFlags(Input::MapSelectionFlags::enableConstruct | Input::MapSelectionFlags::unk_02);
Input::resetMapSelectionFlag(Input::MapSelectionFlags::unk_03);
_constructionArrowPos = Map::Pos3(constructPos.x, constructPos.y, constructHeight);
_constructionArrowDirection = _constructionRotation;
_mapSelectedTiles[0] = constructPos;
_mapSelectedTiles[1].x = -1;
auto pieceId = getPieceId(_lastSelectedTrackPiece, _lastSelectedTrackGradient, _constructionRotation);
if (!pieceId)
{
removeConstructionGhosts();
Map::TileManager::mapInvalidateMapSelectionTiles();
return;
}
_byte_1136065 = pieceId->id;
const auto& trackPieces = getPiece(pieceId->id);
setMapSelectedTilesFromPiece(trackPieces, constructPos, _constructionRotation);
if (_makeJunction != 1)
{
constructHeight = std::max(getMaxConstructHeightFromExistingSelection(), constructHeight);
}
_constructionArrowPos->z = constructHeight - getMaxPieceHeight(trackPieces);
constructHeight -= 16;
auto maxRetries = 2;
if (Input::hasKeyModifier(Input::KeyModifier::shift))
{
maxRetries = 0x80000008;
constructHeight -= 16;
_constructionArrowPos->z = constructHeight;
}
constructionGhostLoop({ constructPos.x, constructPos.y, constructHeight }, maxRetries, getPlacementArgs, placeGhost);
Map::TileManager::mapInvalidateMapSelectionTiles();
}
// 0x0049DC8C
static void onToolUpdate(Window& self, const WidgetIndex_t widgetIndex, const int16_t x, const int16_t y)
{
if (widgetIndex != widx::construct)
{
return;
}
if (_trackType & (1 << 7))
{
onToolUpdateTrack(x, y, getRoadPieceId, tryMakeRoadJunctionAtLoc, TrackData::getRoadPiece, getRoadPlacementArgs, placeRoadGhost);
}
else
{
onToolUpdateTrack(x, y, getTrackPieceId, tryMakeTrackJunctionAtLoc, TrackData::getTrackPiece, getTrackPlacementArgs, placeTrackGhost);
}
}
template<typename TGetPieceId, typename TTryMakeJunction, typename TGetPiece>
static void onToolDownT(const int16_t x, const int16_t y, TGetPieceId&& getPieceId, TTryMakeJunction&& tryMakeJunction, TGetPiece&& getPiece)
{