Interaction rightOver (#1071)

* Start implementing interaction rightOver

* Implement rightOverTrackExtra

* Implement rightOverSignal and TrackStation

* Implement rightOver road extra station

* Implement rightOver airport dock

* Implement rightOver Tree Wall

* Implement rightOver Headquarters BuildingConstruct

* Implement rightOverBuilding remove call

* Minor refactor and naming

* Make gcc happy

* Fix conditional

* Use ternary
This commit is contained in:
Duncan 2021-08-08 20:42:53 +01:00 committed by GitHub
parent bd794b21dc
commit 8333ece44e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 606 additions and 25 deletions

View File

@ -144,6 +144,11 @@ namespace OpenLoco::StringIds
constexpr string_id menu_underground_view = 145;
constexpr string_id menu_hide_foreground_tracks_roads = 146;
constexpr string_id capt_signal = 158;
constexpr string_id capt_station = 159;
constexpr string_id capt_airport = 160;
constexpr string_id capt_ship_port = 161;
constexpr string_id cant_remove_airport = 164;
constexpr string_id cant_remove_ship_port = 165;
constexpr string_id cant_remove_station = 166;
@ -357,6 +362,10 @@ namespace OpenLoco::StringIds
constexpr string_id cant_reverse_train = 388;
constexpr string_id cant_sell_string_id = 390;
constexpr string_id quote_string_quote = 391;
constexpr string_id quote_string_quote2 = 392;
constexpr string_id string_station_platform = 393;
constexpr string_id string_station_building_bus_stop = 394;
constexpr string_id station_catchment = 395;
constexpr string_id menu_mute = 396;
@ -373,7 +382,18 @@ namespace OpenLoco::StringIds
constexpr string_id menu_one_way_direction_arrows = 428;
constexpr string_id menu_town_names_displayed = 429;
constexpr string_id menu_station_names_displayed = 430;
constexpr string_id accepts = 431;
constexpr string_id quantity_eigth = 432;
constexpr string_id quantity_quarter = 433;
constexpr string_id quantity_three_eigths = 434;
constexpr string_id quantity_half = 435;
constexpr string_id quantity_five_eigths = 436;
constexpr string_id quantity_three_quarters = 437;
constexpr string_id quantity_seven_eigths = 438;
constexpr string_id comma = 439;
constexpr string_id produces = 440;
constexpr string_id under_construction = 441;
constexpr string_id string_owned_by_string = 442;
constexpr string_id dropdown_without_checkmark = 443;
constexpr string_id dropdown_with_checkmark = 444;
constexpr string_id error_cant_remove_this = 445;
@ -382,6 +402,8 @@ namespace OpenLoco::StringIds
constexpr string_id cant_plant_this_here = 449;
constexpr string_id outlined_wcolour2_stringid = 450;
constexpr string_id stringid_right_click_to_modify = 451;
constexpr string_id stringid_right_click_to_remove = 452;
constexpr string_id black_tiny_font = 453;
constexpr string_id red_stringid = 454;
@ -1025,6 +1047,7 @@ namespace OpenLoco::StringIds
constexpr string_id prompt_enter_new_name_for_owner = 1460;
constexpr string_id cannot_change_owner_name = 1461;
constexpr string_id headquarters = 1462;
constexpr string_id stringid_headquarters = 1462;
constexpr string_id tooltip_select_company = 1465;

View File

@ -80,12 +80,6 @@ namespace OpenLoco::Map
uint8_t baseZ() const { return _base_z; }
uint8_t clearZ() const { return _clear_z; }
bool hasHighTypeFlag() const { return _type & 0x80; }
void setHighTypeFlag(bool state)
{
_type &= ~0x80;
_type |= state ? 0x80 : 0;
}
bool isGhost() const { return _flags & ElementFlags::ghost; }
bool isFlag5() const { return _flags & ElementFlags::flag_5; }
void setFlag6(bool state)
@ -182,6 +176,12 @@ namespace OpenLoco::Map
_type |= state ? 0x40 : 0;
}
void createWave(int16_t x, int16_t y, int animationIndex);
bool hasHighTypeFlag() const { return _type & 0x80; }
void setHighTypeFlag(bool state)
{
_type &= ~0x80;
_type |= state ? 0x80 : 0;
}
};
struct StationElement : public TileElementBase
@ -192,6 +192,7 @@ namespace OpenLoco::Map
uint16_t _station_id;
public:
uint8_t owner() const { return _4 & 0xF; } // _4l
uint8_t objectId() const { return _5 & 0x1F; }
StationType stationType() const;
uint8_t rotation() const { return _type & 0x3; }
@ -208,7 +209,7 @@ namespace OpenLoco::Map
public:
bool has_40() const { return (_type & 0x40) != 0; }
bool hasStationElement() const { return (_type & 0x80) != 0; }
bool isConstructed() const { return (_type & 0x80) != 0; }
uint8_t colour() const { return _6 >> 11; }
void setColour(Colour_t colour) { _6 = (_6 & 0x7FF) | (colour << 11); }
uint8_t objectId() const { return _4; }
@ -260,7 +261,7 @@ namespace OpenLoco::Map
uint8_t unk_6() const { return _6; } // _6
uint8_t owner() const { return _7 & 0xF; } // _7l
void setOwner(uint8_t newOwner) { _7 = (_7 & 0xF0) | (newOwner & 0xF); }
uint8_t unk_7u() const { return _7 >> 4; } // _7u
bool hasMod(uint8_t mod) const { return _7 & (1 << (4 + mod)); } // _7u
};
struct SignalElement : public TileElementBase
@ -292,7 +293,8 @@ namespace OpenLoco::Map
uint8_t roadObjectId() const { return _5 >> 4; } // _5u
uint8_t sequenceIndex() const { return _5 & 0x3; } // _5l
bool hasStationElement() const { return (_type & 0x80) != 0; }
uint8_t owner() const { return _7 & 0xF; } // _7l
bool hasMod(uint8_t mod) const { return _7 & (1 << (mod + 6)); } // _7u (bits 6 and 7)
uint8_t owner() const { return _7 & 0xF; } // _7l
void setOwner(uint8_t newOwner) { _7 = (_7 & 0xF0) | (newOwner & 0xF); }
};
@ -307,6 +309,7 @@ namespace OpenLoco::Map
OpenLoco::IndustryId_t industryId() const { return _industryId; }
OpenLoco::Industry* industry() const;
uint8_t var_6_1F() const;
bool hasHighTypeFlag() const { return _type & 0x80; } // isConstructed?
};
#pragma pack(pop)

View File

@ -359,7 +359,7 @@ namespace OpenLoco
{
auto buildingEl = el.asBuilding();
if (buildingEl == nullptr || buildingEl->has_40() || !buildingEl->hasStationElement())
if (buildingEl == nullptr || buildingEl->has_40() || !buildingEl->isConstructed())
{
break;
}

View File

@ -168,6 +168,7 @@ namespace OpenLoco::Ui
building = 19,
industry = 20,
headquarterBuilding = 21,
buildingInfo = 22,
};
namespace InteractionItemFlags // Bridge missing?

View File

@ -8,8 +8,14 @@
#include "../Localisation/StringIds.h"
#include "../Localisation/StringManager.h"
#include "../Map/TileManager.h"
#include "../Objects/BuildingObject.h"
#include "../Objects/CargoObject.h"
#include "../Objects/ObjectManager.h"
#include "../Objects/RoadObject.h"
#include "../Objects/TrackExtraObject.h"
#include "../Objects/TrackObject.h"
#include "../Objects/TreeObject.h"
#include "../Objects/WallObject.h"
#include "../Paint/Paint.h"
#include "../StationManager.h"
#include "../TownManager.h"
@ -401,6 +407,467 @@ namespace OpenLoco::Ui::ViewportInteraction
return InteractionArg{};
}
// 0x004CE1D4
static bool rightOverTrack(InteractionArg& interaction)
{
interaction.type = InteractionItem::track;
auto* tileElement = reinterpret_cast<Map::TileElement*>(interaction.object);
auto* track = tileElement->asTrack();
if (track == nullptr)
return false;
if (track->isGhost())
{
return false;
}
if (Ui::Windows::MapToolTip::getTooltipTimeout() < 45)
{
return true;
}
auto* trackObj = ObjectManager::get<TrackObject>(track->trackObjectId());
if (track->owner() == CompanyManager::getControllingId())
{
FormatArguments::mapToolTip(StringIds::stringid_right_click_to_modify, trackObj->name);
}
else
{
auto* company = CompanyManager::get(track->owner());
FormatArguments::mapToolTip(StringIds::string_owned_by_string, trackObj->name, company->name);
Windows::MapToolTip::setOwner(track->owner());
}
return true;
}
// 0x004CE18F
static bool rightOverTrackExtra(InteractionArg& interaction)
{
if (!Windows::Construction::isOverheadTabOpen())
{
return rightOverTrack(interaction);
}
auto* tileElement = reinterpret_cast<Map::TileElement*>(interaction.object);
auto* track = tileElement->asTrack();
if (track == nullptr)
return false;
if (track->isGhost())
{
return false;
}
if (!track->hasMod(interaction.unkBh))
{
return rightOverTrack(interaction);
}
auto* trackObj = ObjectManager::get<TrackObject>(track->trackObjectId());
auto* trackExtraObj = ObjectManager::get<TrackExtraObject>(trackObj->mods[interaction.unkBh]);
FormatArguments::mapToolTip(StringIds::stringid_right_click_to_remove, trackExtraObj->name);
return true;
}
// 0x004CDBEA
static bool rightOverSignal(InteractionArg& interaction)
{
auto* tileElement = reinterpret_cast<Map::TileElement*>(interaction.object);
auto* signal = tileElement->asSignal();
auto* track = (tileElement - 1)->asTrack();
if (signal == nullptr || track == nullptr)
{
return false;
}
if (signal->isGhost())
{
return false;
}
if (!Windows::Construction::isSignalTabOpen())
{
interaction.object = track;
return rightOverTrack(interaction);
}
if (track->owner() != CompanyManager::getControllingId())
{
return false;
}
FormatArguments::mapToolTip(StringIds::stringid_right_click_to_remove, StringIds::capt_signal);
return true;
}
// 0x004CDD8C
static bool rightOverTrackStation(InteractionArg& interaction)
{
auto* tileElement = reinterpret_cast<Map::TileElement*>(interaction.object);
auto* elStation = tileElement->asStation();
auto* track = (tileElement - 1)->asTrack();
if (elStation == nullptr || track == nullptr)
{
return false;
}
if (elStation->isGhost())
{
return false;
}
if (!Windows::Construction::isStationTabOpen())
{
interaction.object = track;
return rightOverTrack(interaction);
}
if (track->owner() != CompanyManager::getControllingId())
{
return false;
}
auto* station = StationManager::get(elStation->stationId());
FormatArguments::mapToolTip(StringIds::stringid_right_click_to_remove, StringIds::string_station_platform, station->name, station->town);
return true;
}
// 0x004CE2C1
static bool rightOverRoad(InteractionArg& interaction)
{
interaction.type = InteractionItem::road;
auto* tileElement = reinterpret_cast<Map::TileElement*>(interaction.object);
auto* road = tileElement->asRoad();
if (road == nullptr)
return false;
if (road->isGhost())
{
return false;
}
if (Ui::Windows::MapToolTip::getTooltipTimeout() < 45)
{
return true;
}
auto* roadObj = ObjectManager::get<RoadObject>(road->roadObjectId());
if (road->owner() == CompanyManager::getControllingId() || road->owner() == CompanyId::neutral)
{
FormatArguments::mapToolTip(StringIds::stringid_right_click_to_modify, roadObj->name);
}
else
{
auto* company = CompanyManager::get(road->owner());
FormatArguments::mapToolTip(StringIds::string_owned_by_string, roadObj->name, company->name);
Windows::MapToolTip::setOwner(road->owner());
}
return true;
}
// 0x004CE271
static bool rightOverRoadExtra(InteractionArg& interaction)
{
if (!Windows::Construction::isOverheadTabOpen())
{
return rightOverRoad(interaction);
}
auto* tileElement = reinterpret_cast<Map::TileElement*>(interaction.object);
auto* road = tileElement->asRoad();
if (road == nullptr)
return false;
if (road->isGhost())
{
return false;
}
if (!road->hasMod(interaction.unkBh))
{
return rightOverRoad(interaction);
}
auto* roadObj = ObjectManager::get<RoadObject>(road->roadObjectId());
auto* roadExtraObj = ObjectManager::get<TrackExtraObject>(roadObj->mods[interaction.unkBh]);
FormatArguments::mapToolTip(StringIds::stringid_right_click_to_remove, roadExtraObj->name);
return true;
}
// 0x004CDDF2
static bool rightOverRoadStation(InteractionArg& interaction)
{
auto* tileElement = reinterpret_cast<Map::TileElement*>(interaction.object);
auto* elStation = tileElement->asStation();
auto* road = (tileElement - 1)->asRoad();
if (elStation == nullptr || road == nullptr)
{
return false;
}
if (elStation->isGhost())
{
return false;
}
if (!Windows::Construction::isStationTabOpen())
{
interaction.object = road;
return rightOverRoad(interaction);
}
if (road->owner() != CompanyManager::getControllingId())
{
return false;
}
auto* station = StationManager::get(elStation->stationId());
FormatArguments::mapToolTip(StringIds::stringid_right_click_to_remove, StringIds::string_station_building_bus_stop, station->name, station->town);
return true;
}
// 0x004CDC26
static bool rightOverAirport(InteractionArg& interaction)
{
auto* tileElement = reinterpret_cast<Map::TileElement*>(interaction.object);
auto* elStation = tileElement->asStation();
if (elStation == nullptr)
{
return false;
}
if (elStation->isGhost())
{
return false;
}
if (elStation->owner() != CompanyManager::getControllingId())
{
return false;
}
auto* station = StationManager::get(elStation->stationId());
auto args = FormatArguments::mapToolTip();
if (Windows::Construction::isStationTabOpen())
{
args.push(StringIds::stringid_right_click_to_remove);
}
else
{
args.push(StringIds::stringid_right_click_to_modify);
}
args.push(StringIds::quote_string_quote);
args.push(station->name);
args.push(station->town);
return true;
}
// 0x004CDCD9
static bool rightOverDock(InteractionArg& interaction)
{
auto* tileElement = reinterpret_cast<Map::TileElement*>(interaction.object);
auto* elStation = tileElement->asStation();
if (elStation == nullptr)
{
return false;
}
if (elStation->isGhost())
{
return false;
}
if (elStation->owner() != CompanyManager::getControllingId())
{
return false;
}
auto* station = StationManager::get(elStation->stationId());
auto args = FormatArguments::mapToolTip();
if (Windows::Construction::isStationTabOpen())
{
args.push(StringIds::stringid_right_click_to_remove);
}
else
{
args.push(StringIds::stringid_right_click_to_modify);
}
args.push(StringIds::quote_string_quote2);
args.push(station->name);
args.push(station->town);
return true;
}
// 0x004CDE58
static bool rightOverTree(InteractionArg& interaction)
{
auto* tileElement = reinterpret_cast<Map::TileElement*>(interaction.object);
auto* tree = tileElement->asTree();
if (tree == nullptr)
{
return false;
}
auto* treeObj = ObjectManager::get<TreeObject>(tree->treeObjectId());
FormatArguments::mapToolTip(StringIds::stringid_right_click_to_remove, treeObj->name);
return true;
}
// 0x004CE0D8
static bool rightOverWall(InteractionArg& interaction)
{
auto* tileElement = reinterpret_cast<Map::TileElement*>(interaction.object);
auto* wall = tileElement->asWall();
if (wall == nullptr)
{
return false;
}
if (!isEditorMode())
{
return false;
}
auto* wallObj = ObjectManager::get<WallObject>(wall->wallObjectId());
FormatArguments::mapToolTip(StringIds::stringid_right_click_to_remove, wallObj->name);
return true;
}
// 0x004CDE78
// Note: this is only when in a constructing mode
static bool rightOverBuildingConstruct(InteractionArg& interaction)
{
auto* tileElement = reinterpret_cast<Map::TileElement*>(interaction.object);
auto* building = tileElement->asBuilding();
if (building == nullptr)
{
return false;
}
auto* buildingObj = building->object();
auto args = FormatArguments::mapToolTip();
if (isEditorMode() || !(buildingObj->flags & BuildingObjectFlags::undestructible))
{
args.push(StringIds::stringid_right_click_to_remove);
}
args.push(buildingObj->name);
return true;
}
// 0x004CE107
static bool rightOverHeadquarters(InteractionArg& interaction)
{
auto* tileElement = reinterpret_cast<Map::TileElement*>(interaction.object);
auto* building = tileElement->asBuilding();
if (building == nullptr)
{
return false;
}
auto firstTile = interaction.pos - Map::offsets[building->multiTileIndex()];
auto height = building->baseZ();
for (auto& company : CompanyManager::companies())
{
if (company.headquarters_x == firstTile.x && company.headquarters_y == firstTile.y && company.headquarters_z == height)
{
FormatArguments::mapToolTip(StringIds::stringid_right_click_to_remove, StringIds::stringid_headquarters, company.name);
return true;
}
}
return false;
}
constexpr std::array<string_id, 7> quantityToString = {
StringIds::quantity_eigth,
StringIds::quantity_quarter,
StringIds::quantity_three_eigths,
StringIds::quantity_half,
StringIds::quantity_five_eigths,
StringIds::quantity_three_quarters,
StringIds::quantity_seven_eigths,
};
// 0x004CDEB8
// Note: this is only when in a none constructing mode
static bool rightOverBuilding(InteractionArg& interaction)
{
interaction.type = InteractionItem::buildingInfo;
if (Ui::Windows::MapToolTip::getTooltipTimeout() < 45)
{
return true;
}
auto* tileElement = reinterpret_cast<Map::TileElement*>(interaction.object);
auto* building = tileElement->asBuilding();
if (building == nullptr)
{
return false;
}
auto* buildingObj = building->object();
auto* buffer = const_cast<char*>(StringManager::getString(StringIds::buffer_338));
buffer = StringManager::formatString(buffer, buildingObj->name);
if (!building->isConstructed())
{
buffer = StringManager::formatString(buffer, StringIds::under_construction);
}
else
{
if (buildingObj->var_A0[0] != 0 || buildingObj->var_A0[1] != 0)
{
buffer = StringManager::formatString(buffer, StringIds::produces);
bool requiresComma = false;
for (auto i = 0; i < 2; ++i)
{
if (buildingObj->var_A0[i] != 0)
{
if (requiresComma)
{
buffer = StringManager::formatString(buffer, StringIds::comma);
}
requiresComma = true;
auto* cargo = ObjectManager::get<CargoObject>(buildingObj->producedCargoType[i]);
buffer = StringManager::formatString(buffer, cargo->name);
}
}
}
if (buildingObj->var_A6[0] != 0 || buildingObj->var_A6[1] != 0 || buildingObj->var_A8[0] != 0 || buildingObj->var_A8[1] != 0)
{
buffer = StringManager::formatString(buffer, StringIds::accepts);
bool requiresComma = false;
for (auto i = 0; i < 2; ++i)
{
if (buildingObj->var_A6[i] != 0)
{
if (requiresComma)
{
buffer = StringManager::formatString(buffer, StringIds::comma);
}
if (buildingObj->var_A6[i] < 8)
{
buffer = StringManager::formatString(buffer, quantityToString[buildingObj->var_A6[i]]);
}
requiresComma = true;
auto* cargo = ObjectManager::get<CargoObject>(buildingObj->producedCargoType[i]);
buffer = StringManager::formatString(buffer, cargo->name);
}
}
for (auto i = 0; i < 2; ++i)
{
if (buildingObj->var_A8[i] != 0)
{
if (requiresComma)
{
buffer = StringManager::formatString(buffer, StringIds::comma);
}
if (buildingObj->var_A8[i] < 8)
{
buffer = StringManager::formatString(buffer, quantityToString[buildingObj->var_A8[i]]);
}
requiresComma = true;
auto* cargo = ObjectManager::get<CargoObject>(buildingObj->var_A4[i]);
buffer = StringManager::formatString(buffer, cargo->name);
}
}
}
}
FormatArguments::mapToolTip(StringIds::buffer_338);
return true;
}
// 0x004CDB2B
InteractionArg rightOver(int16_t x, int16_t y)
{
@ -428,20 +895,75 @@ namespace OpenLoco::Ui::ViewportInteraction
}
}
registers regs;
regs.ax = x;
regs.bx = y;
regs.edx = interactionsToExclude;
call(0x004CDB3F, regs);
InteractionArg result;
result.value = regs.edx;
result.pos.x = regs.ax;
result.pos.y = regs.cx;
result.unkBh = regs.bh;
result.type = static_cast<InteractionItem>(regs.bl);
return result;
bool hasInteraction = false;
auto res = getMapCoordinatesFromPos(x, y, interactionsToExclude);
auto& interaction = res.first;
switch (interaction.type)
{
case InteractionItem::track:
hasInteraction = rightOverTrack(interaction);
break;
case InteractionItem::entity:
// No interaction
break;
case InteractionItem::trackExtra:
hasInteraction = rightOverTrackExtra(interaction);
break;
case InteractionItem::signal:
hasInteraction = rightOverSignal(interaction);
break;
case InteractionItem::trackStation:
hasInteraction = rightOverTrackStation(interaction);
break;
case InteractionItem::roadStation:
hasInteraction = rightOverRoadStation(interaction);
break;
case InteractionItem::airport:
hasInteraction = rightOverAirport(interaction);
break;
case InteractionItem::dock:
hasInteraction = rightOverDock(interaction);
break;
case InteractionItem::road:
hasInteraction = rightOverRoad(interaction);
break;
case InteractionItem::roadExtra:
hasInteraction = rightOverRoadExtra(interaction);
break;
default:
if (!(Input::hasFlag(Input::Flags::toolActive) && Input::hasFlag(Input::Flags::flag6)))
{
if (WindowManager::find(WindowType::construction) == nullptr)
{
if (interaction.type == InteractionItem::building)
{
hasInteraction = rightOverBuilding(interaction);
}
break;
}
}
// 0x4CDBC5
switch (interaction.type)
{
case InteractionItem::tree:
hasInteraction = rightOverTree(interaction);
break;
case InteractionItem::wall:
hasInteraction = rightOverWall(interaction);
break;
case InteractionItem::building:
hasInteraction = rightOverBuildingConstruct(interaction);
break;
case InteractionItem::headquarterBuilding:
hasInteraction = rightOverHeadquarters(interaction);
break;
default:
hasInteraction = true;
break;
}
break;
}
return hasInteraction ? interaction : InteractionArg{};
}
// 0x00459E54

View File

@ -135,6 +135,8 @@ namespace OpenLoco::Ui::Windows
void setToRoadExtra(Window* main, Map::RoadElement* track, const uint8_t bh, const Map::Pos2 pos);
void sub_4A6FAC();
bool isStationTabOpen();
bool isOverheadTabOpen();
bool isSignalTabOpen();
bool rotate(Window* self);
void removeConstructionGhosts();
void registerHooks();
@ -191,6 +193,7 @@ namespace OpenLoco::Ui::Windows
{
void open();
void setOwner(CompanyId_t company);
uint16_t getTooltipTimeout();
void reset();
}

View File

@ -362,6 +362,30 @@ namespace OpenLoco::Ui::Windows::Construction
return window->current_tab == Common::widx::tab_station - Common::widx::tab_construction;
}
// 0x004A69EE
bool isOverheadTabOpen()
{
auto* window = WindowManager::find(WindowType::construction);
if (window == nullptr)
{
return false;
}
return window->current_tab == Common::widx::tab_overhead - Common::widx::tab_construction;
}
// 0x004A6A2A
bool isSignalTabOpen()
{
auto* window = WindowManager::find(WindowType::construction);
if (window == nullptr)
{
return false;
}
return window->current_tab == Common::widx::tab_signal - Common::widx::tab_construction;
}
// 0x0049FEC7
void removeConstructionGhosts()
{

View File

@ -94,6 +94,11 @@ namespace OpenLoco::Ui::Windows::MapToolTip
_mapTooltipOwner = company;
}
uint16_t getTooltipTimeout()
{
return _mapTooltipTimeout;
}
void reset()
{
setOwner(CompanyId::null);