Reimplement station building previews

This commit is contained in:
dP
2025-06-26 18:24:01 +05:00
parent c219bf3e4f
commit b2737fe293
20 changed files with 1587 additions and 1057 deletions

View File

@@ -24,6 +24,17 @@ FILES = [
'script/script_cmd.h', 'script/script_cmd.h',
] ]
BASE_CLASS = {
'BuildDock': 'StationBuildCommand',
'BuildRailStation': 'StationBuildCommand',
'BuildAirport': 'StationBuildCommand',
'BuildRoadStop': 'StationBuildCommand',
}
BASE_FIELDS = {
'StationBuildCommand': ['station_to_join', 'adjacent'],
}
BASE_DIR = Path(__file__).parent BASE_DIR = Path(__file__).parent
OUTPUT = BASE_DIR / 'src/citymania/generated/cm_gen_commands' OUTPUT = BASE_DIR / 'src/citymania/generated/cm_gen_commands'
GLOBAL_TYPES = set(('GoalType', 'GoalTypeID', 'GoalID')) GLOBAL_TYPES = set(('GoalType', 'GoalTypeID', 'GoalID'))
@@ -245,13 +256,20 @@ def run():
) )
for cmd in commands: for cmd in commands:
name = cmd['name'] name = cmd['name']
args_list = ', '.join(f'{at} {an}' for at, an in cmd['args']) base_class = BASE_CLASS.get(name, 'Command')
args_init = ', '.join(f'{an}{{{an}}}' for _, an in cmd['args']) base_fields = BASE_FIELDS.get(base_class, [])
f.write( f.write(
f'class {name}: public Command {{\n' f'class {name}: public {base_class} {{\n'
f'public:\n' f'public:\n'
) )
args_list = ', '.join(f'{at} {an}' for at, an in cmd['args'])
args_init = ', '.join(f'{an}{{{an}}}' for _, an in cmd['args'] if an not in base_fields)
if base_fields:
base_joined = ', '.join(base_fields)
args_init = f'{base_class}{{{base_joined}}}, ' + args_init
for at, an in cmd['args']: for at, an in cmd['args']:
if an in base_fields:
continue
f.write(f' {at} {an};\n') f.write(f' {at} {an};\n')
f.write(f'\n') f.write(f'\n')
if args_init: if args_init:

View File

@@ -37,6 +37,7 @@
#include "widgets/airport_widget.h" #include "widgets/airport_widget.h"
#include "citymania/cm_hotkeys.hpp" #include "citymania/cm_hotkeys.hpp"
#include "citymania/cm_highlight.hpp"
#include "citymania/cm_station_gui.hpp" #include "citymania/cm_station_gui.hpp"
#include "safeguards.h" #include "safeguards.h"
@@ -64,11 +65,7 @@ void CcBuildAirport(Commands, const CommandCost &result, TileIndex tile)
*/ */
static void PlaceAirport(TileIndex tile) static void PlaceAirport(TileIndex tile)
{ {
if (citymania::UseImprovedStationJoin()) { NOT_REACHED(); // CityMania uses tools
citymania::PlaceAirport(tile);
return;
}
if (_selected_airport_index == -1) return; if (_selected_airport_index == -1) return;
byte airport_type = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index)->GetIndex(); byte airport_type = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index)->GetIndex();
@@ -130,7 +127,8 @@ struct BuildAirToolbarWindow : Window {
{ {
switch (widget) { switch (widget) {
case WID_AT_AIRPORT: case WID_AT_AIRPORT:
if (HandlePlacePushButton(this, WID_AT_AIRPORT, SPR_CURSOR_AIRPORT, HT_RECT, CM_DDSP_BUILD_AIRPORT)) { // if (HandlePlacePushButton(this, WID_AT_AIRPORT, SPR_CURSOR_AIRPORT, HT_RECT, CM_DDSP_BUILD_AIRPORT)) {
if (citymania::HandlePlacePushButton(this, WID_AT_AIRPORT, std::make_unique<citymania::AirportBuildTool>())) {
ShowBuildAirportPicker(this); ShowBuildAirportPicker(this);
this->last_user_action = widget; this->last_user_action = widget;
} }

View File

@@ -94,6 +94,15 @@ public:
} }
}; };
class StationBuildCommand : public Command {
public:
StationID station_to_join;
bool adjacent;
StationBuildCommand(StationID station_to_join, bool adjacent)
:station_to_join{station_to_join}, adjacent{adjacent} {}
};
} // namaespace citymania } // namaespace citymania
#endif #endif

View File

@@ -188,7 +188,6 @@ struct TileZoning {
static TileZoning *_mz = nullptr; static TileZoning *_mz = nullptr;
static IndustryType _industry_forbidden_tiles = INVALID_INDUSTRYTYPE; static IndustryType _industry_forbidden_tiles = INVALID_INDUSTRYTYPE;
extern StationBuildingStatus _station_building_status;
extern const Station *_station_to_join; extern const Station *_station_to_join;
extern const Station *_highlight_station_to_join; extern const Station *_highlight_station_to_join;
extern TileArea _highlight_join_area; extern TileArea _highlight_join_area;
@@ -509,6 +508,13 @@ ObjectHighlight ObjectHighlight::make_industry(TileIndex tile, IndustryType ind_
return oh; return oh;
} }
ObjectHighlight ObjectHighlight::make_dock(TileIndex tile, DiagDirection orientation) {
auto oh = ObjectHighlight{ObjectHighlight::Type::DOCK};
oh.tile = tile;
oh.ddir = orientation;
return oh;
}
/** /**
* Try to add an additional rail-track at the entrance of a depot * Try to add an additional rail-track at the entrance of a depot
* @param tile Tile to use for adding the rail-track * @param tile Tile to use for adding the rail-track
@@ -561,12 +567,12 @@ void ObjectHighlight::AddStationOverlayData(int w, int h, int rad, StationCovera
if (cs == nullptr) continue; if (cs == nullptr) continue;
if (!has_header) { if (!has_header) {
this->overlay_data.emplace_back(PAL_NONE, GetString(CM_STR_BUILD_INFO_OVERLAY_STATION_SUPPLIES)); this->overlay_data.emplace_back(0, PAL_NONE, GetString(CM_STR_BUILD_INFO_OVERLAY_STATION_SUPPLIES));
has_header = true; has_header = true;
} }
SetDParam(0, i); SetDParam(0, i);
SetDParam(1, production[i] >> 8); SetDParam(1, production[i] >> 8);
this->overlay_data.emplace_back(cs->GetCargoIcon(), GetString(CM_STR_BUILD_INFO_OVERLAY_STATION_CARGO)); this->overlay_data.emplace_back(1, cs->GetCargoIcon(), GetString(CM_STR_BUILD_INFO_OVERLAY_STATION_CARGO));
} }
} }
@@ -802,6 +808,20 @@ void ObjectHighlight::UpdateTiles() {
} }
break; break;
} }
case Type::DOCK: {
this->cost = cmd::BuildDock(
this->tile,
NEW_STATION,
true
).test();
auto palette = (cost.Succeeded() ? CM_PALETTE_TINT_WHITE : CM_PALETTE_TINT_RED_DEEP);
this->AddTile(this->tile, ObjectTileHighlight::make_dock_slope(palette, this->ddir));
if (this->ddir != INVALID_DIAGDIR) {
TileIndex tile_to = TileAddByDiagDir(this->tile, this->ddir);
this->AddTile(tile_to, ObjectTileHighlight::make_dock_flat(palette, DiagDirToAxis(this->ddir)));
}
break;
}
default: default:
NOT_REACHED(); NOT_REACHED();
} }
@@ -818,11 +838,12 @@ void ObjectHighlight::UpdateOverlay() {
HideBuildInfoOverlay(); HideBuildInfoOverlay();
return; return;
} }
auto err = this->cost.GetErrorMessage(); auto err = this->cost.GetErrorMessage();
// auto extra_err = this->cost.GetExtraErrorMessage(); // auto extra_err = this->cost.GetExtraErrorMessage();
bool no_money = (err == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY); bool no_money = (err == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY);
SetDParam(0, this->cost.GetCost()); SetDParam(0, this->cost.GetCost());
this->overlay_data.emplace_back(PAL_NONE, GetString(no_money ? CM_STR_BUILD_INFO_OVERLAY_COST_NO_MONEY : CM_STR_BUILD_INFO_OVERLAY_COST_OK)); this->overlay_data.emplace_back(0, PAL_NONE, GetString(no_money ? CM_STR_BUILD_INFO_OVERLAY_COST_NO_MONEY : CM_STR_BUILD_INFO_OVERLAY_COST_OK));
// if (this->cost.Failed() && err != STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) { // if (this->cost.Failed() && err != STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) {
// if (err == INVALID_STRING_ID) { // if (err == INVALID_STRING_ID) {
// this->overlay_data.emplace_back(PAL_NONE, GetString(CM_STR_BUILD_INFO_OVERLAY_ERROR_UNKNOWN)); // this->overlay_data.emplace_back(PAL_NONE, GetString(CM_STR_BUILD_INFO_OVERLAY_ERROR_UNKNOWN));
@@ -1619,6 +1640,44 @@ TileHighlight ObjectHighlight::GetTileHighlight(const TileInfo *ti) {
return th; return th;
} }
HighlightMap ObjectHighlight::GetHighlightMap(SpriteID palette) {
// TODO remove the need to convert (maybe replace HighlightMap with multimap?)
HighlightMap res;
for (auto &[tile, oth] : this->tiles) {
auto othp = oth;
othp.palette = palette;
res.Add(tile, othp);
}
return res;
}
std::optional<TileArea> ObjectHighlight::GetArea() {
switch(this->type) {
case Type::NONE:
case Type::BLUEPRINT:
case Type::POLYRAIL:
case Type::INDUSTRY:
return std::nullopt;
case Type::RAIL_DEPOT:
case Type::ROAD_DEPOT:
return TileArea{this->tile, 1, 1};
case Type::RAIL_STATION:
case Type::ROAD_STOP:
return TileArea{this->tile, this->end_tile};
case Type::AIRPORT: {
const AirportSpec *as = AirportSpec::Get(this->airport_type);
if (!as->IsAvailable() || this->airport_layout >= as->num_table) return std::nullopt;
return TileArea{this->tile, as->size_x, as->size_y};
}
case Type::DOCK: {
if (this->ddir == INVALID_DIAGDIR) return std::nullopt;
return TileArea{this->tile, TileAddByDiagDir(this->tile, this->ddir)};
}
default:
NOT_REACHED();
}
}
static void DrawObjectTileHighlight(const TileInfo *ti, const ObjectTileHighlight &oth) { static void DrawObjectTileHighlight(const TileInfo *ti, const ObjectTileHighlight &oth) {
switch (oth.type) { switch (oth.type) {
case ObjectTileHighlight::Type::RAIL_DEPOT: case ObjectTileHighlight::Type::RAIL_DEPOT:
@@ -1917,7 +1976,7 @@ void CalcCBTownLimitBorder(TileHighlight &th, TileIndex tile, SpriteID border_pa
TileHighlight GetTileHighlight(const TileInfo *ti, TileType tile_type) { TileHighlight GetTileHighlight(const TileInfo *ti, TileType tile_type) {
TileHighlight th; TileHighlight th;
auto hl = _ap.tiles.GetForTile(ti->tile); auto hl = _at.tiles.GetForTile(ti->tile);
if (hl.has_value()) { if (hl.has_value()) {
for (auto &oth : hl.value().get()) { for (auto &oth : hl.value().get()) {
oth.SetTileHighlight(th, ti); oth.SetTileHighlight(th, ti);
@@ -2046,7 +2105,7 @@ void DrawTileZoning(const TileInfo *ti, const TileHighlight &th, TileType tile_t
bool DrawTileSelection(const TileInfo *ti, [[maybe_unused]] const TileHighlightType &tht) { bool DrawTileSelection(const TileInfo *ti, [[maybe_unused]] const TileHighlightType &tht) {
if (ti->tile == INVALID_TILE || IsTileType(ti->tile, MP_VOID)) return false; if (ti->tile == INVALID_TILE || IsTileType(ti->tile, MP_VOID)) return false;
auto hl = _ap.tiles.GetForTile(ti->tile); auto hl = _at.tiles.GetForTile(ti->tile);
if (hl.has_value()) { if (hl.has_value()) {
for (auto &oth : hl.value().get()) { for (auto &oth : hl.value().get()) {
DrawObjectTileHighlight(ti, oth); DrawObjectTileHighlight(ti, oth);
@@ -2376,7 +2435,8 @@ PaletteID GetTreeShadePal(TileIndex tile) {
} }
} }
ActivePreview _ap; ActiveTool _at;
static void ResetVanillaHighlight() { static void ResetVanillaHighlight() {
if (_thd.window_class != WC_INVALID) { if (_thd.window_class != WC_INVALID) {
@@ -2402,34 +2462,81 @@ static void ResetVanillaHighlight() {
_thd.make_square_red = false; _thd.make_square_red = false;
} }
void SetActivePreview(up<Preview> &&preview) { void SetActiveTool(up<Tool> &&tool) {
ResetVanillaHighlight(); ResetVanillaHighlight();
ResetActivePreview(); ResetActiveTool();
_ap.preview = std::move(preview); _at.tool = std::move(tool);
} }
void ResetActivePreview() { void ResetActiveTool() {
for (auto t : _ap.tiles.GetAllTiles()) { for (auto t : _at.tiles.GetAllTiles()) {
MarkTileDirtyByTile(t); MarkTileDirtyByTile(t);
} }
_ap.preview = nullptr; _at.tool = nullptr;
_ap.tiles = {}; _at.tiles = {};
} }
const up<Preview> &GetActivePreview() { const up<Tool> &GetActiveTool() {
return _ap.preview; return _at.tool;
} }
void UpdateActivePreview() { void UpdateActiveTool() {
if (_ap.preview == nullptr) return;
Point pt = GetTileBelowCursor(); Point pt = GetTileBelowCursor();
auto tile = pt.x == -1 ? INVALID_TILE : TileVirtXY(pt.x, pt.y); auto tile = pt.x == -1 ? INVALID_TILE : TileVirtXY(pt.x, pt.y);
_ap.preview->Update(pt, tile);
auto tiles_changed = _ap.tiles.UpdateWithMap(_ap.preview->GetHighlightMap()); ToolGUIInfo info;
if (citymania::StationBuildTool::active_highlight.has_value()) {
info = GetSelectedStationGUIInfo();
} else if (_at.tool != nullptr) {
_at.tool->Update(pt, tile);
info = _at.tool->GetGUIInfo();
}
auto [hlmap, overlay_data, cost] = info;
auto tiles_changed = _at.tiles.UpdateWithMap(hlmap);
for (auto t : tiles_changed) for (auto t : tiles_changed)
MarkTileDirtyByTile(t); MarkTileDirtyByTile(t);
if (cost.GetExpensesType() != INVALID_EXPENSES || cost.GetErrorMessage() != INVALID_STRING_ID) {
// Add CommandCost info
auto err = cost.GetErrorMessage();
if (cost.Succeeded()) {
auto money = cost.GetCost();
if (money != 0) {
SetDParam(0, money);
overlay_data.emplace_back(0, PAL_NONE, GetString(CM_STR_BUILD_INFO_OVERLAY_COST_OK));
}
} else if (err == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) {
SetDParam(0, cost.GetCost());
overlay_data.emplace_back(0, PAL_NONE, GetString(CM_STR_BUILD_INFO_OVERLAY_COST_NO_MONEY));
} else {
if (err == INVALID_STRING_ID) {
overlay_data.emplace_back(0, PAL_NONE, GetString(CM_STR_BUILD_INFO_OVERLAY_ERROR_UNKNOWN));
} else {
SetDParam(0, err);
overlay_data.emplace_back(0, PAL_NONE, GetString(CM_STR_BUILD_INFO_OVERLAY_ERROR));
}
auto extra_err = cost.GetExtraErrorMessage();
if (extra_err != INVALID_STRING_ID) {
SetDParam(0, extra_err);
overlay_data.emplace_back(0, PAL_NONE, GetString(CM_STR_BUILD_INFO_OVERLAY_ERROR));
}
}
}
/* Update overlay */
if (overlay_data.size() > 0) {
auto w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y);
if (w == nullptr) { HideBuildInfoOverlay(); return; }
auto vp = IsPtInWindowViewport(w, _cursor.pos.x, _cursor.pos.y);
if (vp == nullptr) { HideBuildInfoOverlay(); return; }
Point pto = RemapCoords2(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE);
pto.x = UnScaleByZoom(pto.x - vp->virtual_left, vp->zoom) + vp->left;
pto.y = UnScaleByZoom(pto.y - vp->virtual_top, vp->zoom) + vp->top;
ShowBuildInfoOverlay(pto.x, pto.y, overlay_data);
} else {
HideBuildInfoOverlay();
}
} }
bool _prev_left_button_down = false; bool _prev_left_button_down = false;
@@ -2444,35 +2551,35 @@ bool HandleMouseMove() {
bool released = !_left_button_down && changed && _keep_mouse_click; bool released = !_left_button_down && changed && _keep_mouse_click;
if (!_left_button_down) _keep_mouse_click = false; if (!_left_button_down) _keep_mouse_click = false;
if (_ap.preview == nullptr) return false; if (_at.tool == nullptr) return false;
// Viewport *vp = IsPtInWindowViewport(w, ); // Viewport *vp = IsPtInWindowViewport(w, );
auto pt = GetTileBelowCursor(); auto pt = GetTileBelowCursor();
if (pt.x == -1) return false; if (pt.x == -1) return false;
auto tile = pt.x == -1 ? INVALID_TILE : TileVirtXY(pt.x, pt.y); auto tile = pt.x == -1 ? INVALID_TILE : TileVirtXY(pt.x, pt.y);
_ap.preview->Update(pt, tile); _at.tool->Update(pt, tile);
_ap.preview->HandleMouseMove(); _at.tool->HandleMouseMove();
if (_left_button_down) { if (_left_button_down) {
if (changed && _ap.preview->HandleMousePress()) { if (changed && _at.tool->HandleMousePress()) {
_keep_mouse_click = true; _keep_mouse_click = true;
} }
if (_keep_mouse_click) return true; if (_keep_mouse_click) return true;
} }
if (released) { if (released) {
_ap.preview->HandleMouseRelease(); _at.tool->HandleMouseRelease();
} }
return false; return false;
} }
bool HandleMouseClick(Viewport *vp, bool double_click) { bool HandleMouseClick(Viewport *vp, bool double_click) {
if (_ap.preview == nullptr) return false; if (_at.tool == nullptr) return false;
auto pt = GetTileBelowCursor(); auto pt = GetTileBelowCursor();
auto tile = pt.x == -1 ? INVALID_TILE : TileVirtXY(pt.x, pt.y); auto tile = pt.x == -1 ? INVALID_TILE : TileVirtXY(pt.x, pt.y);
_ap.preview->Update(pt, tile); _at.tool->Update(pt, tile);
return _ap.preview->HandleMouseClick(vp, pt, tile, double_click); return _at.tool->HandleMouseClick(vp, pt, tile, double_click);
} }
bool HandlePlacePushButton(Window *w, WidgetID widget, up<Preview> preview) { bool HandlePlacePushButton(Window *w, WidgetID widget, up<Tool> tool) {
if (w->IsWidgetDisabled(widget)) return false; if (w->IsWidgetDisabled(widget)) return false;
if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
@@ -2485,13 +2592,13 @@ bool HandlePlacePushButton(Window *w, WidgetID widget, up<Preview> preview) {
w->LowerWidget(widget); w->LowerWidget(widget);
auto icon = preview->GetCursor(); auto icon = tool->GetCursor();
if ((icon & ANIMCURSOR_FLAG) != 0) { if ((icon & ANIMCURSOR_FLAG) != 0) {
SetAnimatedMouseCursor(_animcursors[icon & ~ANIMCURSOR_FLAG]); SetAnimatedMouseCursor(_animcursors[icon & ~ANIMCURSOR_FLAG]);
} else { } else {
SetMouseCursor(icon, PAL_NONE); SetMouseCursor(icon, PAL_NONE);
} }
citymania::SetActivePreview(std::move(preview)); citymania::SetActiveTool(std::move(tool));
_thd.window_class = w->window_class; _thd.window_class = w->window_class;
_thd.window_number = w->window_number; _thd.window_number = w->window_number;
@@ -2499,5 +2606,4 @@ bool HandlePlacePushButton(Window *w, WidgetID widget, up<Preview> preview) {
} }
} // namespace citymania } // namespace citymania

View File

@@ -57,13 +57,13 @@ PaletteID GetTreeShadePal(TileIndex tile);
void RotateAutodetection(); void RotateAutodetection();
void ResetRotateAutodetection(); void ResetRotateAutodetection();
void ResetActivePreview(); void ResetActiveTool();
void SetActivePreview(up<Preview> &&preview); void SetActiveTool(up<Tool> &&tool);
void UpdateActivePreview(); void UpdateActiveTool();
const up<Preview> &GetActivePreview(); const up<Tool> &GetActiveTool();
bool HandlePlacePushButton(Window *w, WidgetID widget, up<Preview> preview); bool HandlePlacePushButton(Window *w, WidgetID widget, up<Tool> tool);
bool HandleMouseMove(); bool HandleMouseMove();
bool HandleMouseClick(Viewport *vp, bool double_click); bool HandleMouseClick(Viewport *vp, bool double_click);

View File

@@ -23,6 +23,7 @@
#include <vector> #include <vector>
#include "cm_command_type.hpp" #include "cm_command_type.hpp"
#include "cm_overlays.hpp"
namespace citymania { namespace citymania {
@@ -313,6 +314,26 @@ public:
std::multimap<TileIndex, ObjectTileHighlight> GetTiles(TileIndex tile); std::multimap<TileIndex, ObjectTileHighlight> GetTiles(TileIndex tile);
}; };
class HighlightMap {
public:
typedef std::map<TileIndex, std::vector<ObjectTileHighlight>> MapType;
typedef decltype(std::views::keys(std::declval<const MapType &>())) MapTypeKeys;
protected:
MapType map;
public:
const MapType &GetMap() const;
void Add(TileIndex tile, ObjectTileHighlight oth);
bool Contains(TileIndex tile) const;
std::optional<std::reference_wrapper<const std::vector<ObjectTileHighlight>>> GetForTile(TileIndex tile) const;
MapTypeKeys GetAllTiles() const;
std::vector<TileIndex> UpdateWithMap(const HighlightMap &update);
void AddTileArea(const TileArea &area, SpriteID palette);
void AddTileAreaWithBorder(const TileArea &area, SpriteID palette);
void AddTilesBorder(const std::set<TileIndex> &tiles, SpriteID palette);
};
class ObjectHighlight { class ObjectHighlight {
public: public:
enum class Type : byte { enum class Type : byte {
@@ -325,6 +346,7 @@ public:
BLUEPRINT = 6, BLUEPRINT = 6,
POLYRAIL = 7, POLYRAIL = 7,
INDUSTRY = 8, INDUSTRY = 8,
DOCK = 9,
}; };
Type type = Type::NONE; Type type = Type::NONE;
@@ -352,7 +374,7 @@ protected:
bool tiles_updated = false; bool tiles_updated = false;
std::multimap<TileIndex, ObjectTileHighlight> tiles; std::multimap<TileIndex, ObjectTileHighlight> tiles;
std::vector<DetachedHighlight> sprites = {}; std::vector<DetachedHighlight> sprites = {};
std::vector<std::pair<SpriteID, std::string>> overlay_data = {}; BuildInfoOverlayData overlay_data = {};
// Point overlay_pos = {0, 0}; // Point overlay_pos = {0, 0};
void AddTile(TileIndex tile, ObjectTileHighlight &&oh); void AddTile(TileIndex tile, ObjectTileHighlight &&oh);
// void AddSprite(TileIndex tile, ObjectTileHighlight &&oh); // void AddSprite(TileIndex tile, ObjectTileHighlight &&oh);
@@ -373,8 +395,11 @@ public:
TileIndex start_tile2, TileIndex end_tile2, Trackdir trackdir2); TileIndex start_tile2, TileIndex end_tile2, Trackdir trackdir2);
static ObjectHighlight make_industry(TileIndex tile, IndustryType ind_type, uint32 ind_layout); static ObjectHighlight make_industry(TileIndex tile, IndustryType ind_type, uint32 ind_layout);
static ObjectHighlight make_dock(TileIndex tile, DiagDirection orientation);
TileHighlight GetTileHighlight(const TileInfo *ti); TileHighlight GetTileHighlight(const TileInfo *ti);
HighlightMap GetHighlightMap(SpriteID palette);
std::optional<TileArea> GetArea();
void Draw(const TileInfo *ti); void Draw(const TileInfo *ti);
void DrawSelectionOverlay(DrawPixelInfo *dpi); void DrawSelectionOverlay(DrawPixelInfo *dpi);
void DrawOverlay(DrawPixelInfo *dpi); void DrawOverlay(DrawPixelInfo *dpi);
@@ -384,24 +409,7 @@ public:
void MarkDirty(); void MarkDirty();
}; };
typedef std::tuple<HighlightMap, BuildInfoOverlayData, CommandCost> ToolGUIInfo;
class HighlightMap {
public:
typedef std::map<TileIndex, std::vector<ObjectTileHighlight>> MapType;
typedef decltype(std::views::keys(std::declval<const MapType &>())) MapTypeKeys;
protected:
MapType map;
public:
const MapType &GetMap() const;
void Add(TileIndex tile, ObjectTileHighlight oth);
bool Contains(TileIndex tile) const;
std::optional<std::reference_wrapper<const std::vector<ObjectTileHighlight>>> GetForTile(TileIndex tile) const;
MapTypeKeys GetAllTiles() const;
std::vector<TileIndex> UpdateWithMap(const HighlightMap &update);
void AddTileArea(const TileArea &area, SpriteID palette);
void AddTileAreaWithBorder(const TileArea &area, SpriteID palette);
void AddTilesBorder(const std::set<TileIndex> &tiles, SpriteID palette);
};
class Preview { class Preview {
public: public:
@@ -411,7 +419,38 @@ public:
virtual bool HandleMousePress() { return false; }; virtual bool HandleMousePress() { return false; };
virtual void HandleMouseRelease() {}; virtual void HandleMouseRelease() {};
virtual bool HandleMouseClick(Viewport* /* vp */, Point /* pt */, TileIndex /* tile */, bool /* double_click */) { return false; }; virtual bool HandleMouseClick(Viewport* /* vp */, Point /* pt */, TileIndex /* tile */, bool /* double_click */) { return false; };
virtual HighlightMap GetHighlightMap() = 0; virtual std::pair<HighlightMap, BuildInfoOverlayData> GetGUIInfo() = 0;
virtual CursorID GetCursor() = 0;
virtual void OnStationRemoved(const Station* /* station */) {};
};
class Action {
public:
virtual ~Action() = default;
virtual void Update(Point pt, TileIndex tile) = 0;
virtual std::optional<TileArea> GetArea() const { return std::nullopt; };
virtual void HandleMouseMove() {};
virtual bool HandleMousePress() { return false; };
virtual void HandleMouseRelease() {};
virtual bool HandleMouseClick(Viewport* vp, Point pt, TileIndex tile, bool double_click) {
(void)vp; (void)pt; (void)tile; (void)double_click;
return false;
};
virtual ToolGUIInfo GetGUIInfo() = 0;
virtual void OnStationRemoved(const Station *);
};
class Tool {
protected:
up<Action> action = nullptr;
public:
virtual ~Tool() = default;
virtual void Update(Point pt, TileIndex tile) = 0;
virtual void HandleMouseMove() { if(this->action) this->action->HandleMouseMove(); };
virtual bool HandleMousePress() { return this->action ? this->action->HandleMousePress() : false; }
virtual void HandleMouseRelease() { if(this->action) this->action->HandleMouseRelease(); };
virtual bool HandleMouseClick(Viewport* vp, Point pt, TileIndex tile, bool double_click) { return this->action ? this->action->HandleMouseClick(vp, pt, tile, double_click) : false; };
virtual ToolGUIInfo GetGUIInfo() = 0;
virtual CursorID GetCursor() = 0; virtual CursorID GetCursor() = 0;
virtual void OnStationRemoved(const Station* /* station */) {}; virtual void OnStationRemoved(const Station* /* station */) {};
}; };
@@ -423,12 +462,13 @@ public:
// DragStop, // DragStop,
// }; // };
struct ActivePreview { struct ActiveTool {
up<Preview> preview; up<Tool> tool;
HighlightMap tiles; HighlightMap tiles;
}; };
extern ActivePreview _ap; extern ActiveTool _at;
} // namespace citymania } // namespace citymania

View File

@@ -62,7 +62,7 @@ public:
} }
void UpdateSize() { void UpdateSize() {
this->padding = ScaleGUITrad(3); this->padding = ScaleGUITrad(5);
auto dim = this->GetContentDimension(); auto dim = this->GetContentDimension();
this->box.width = dim.width + 2 * this->padding; this->box.width = dim.width + 2 * this->padding;
this->box.height = dim.height + 2 * this->padding; this->box.height = dim.height + 2 * this->padding;
@@ -181,8 +181,9 @@ public:
Dimension text_dim{0, 0}; Dimension text_dim{0, 0};
Dimension icon_dim{0, 0}; Dimension icon_dim{0, 0};
for (const auto &[icon, s] : data) { for (const auto &[indent, icon, s] : data) {
text_dim = maxdim(text_dim, GetStringBoundingBox(s)); text_dim = maxdim(text_dim, GetStringBoundingBox(s));
text_dim.width += WidgetDimensions::scaled.hsep_indent * indent;
if (icon != PAL_NONE) if (icon != PAL_NONE)
icon_dim = maxdim(icon_dim, GetSpriteSize(icon)); icon_dim = maxdim(icon_dim, GetSpriteSize(icon));
} }
@@ -192,25 +193,26 @@ public:
if (icon_dim.width > 0) if (icon_dim.width > 0)
this->text_ofs_x = icon_dim.width + this->padding; this->text_ofs_x = icon_dim.width + this->padding;
return { return {
text_dim.width + this->text_ofs_x, std::min<uint>(text_dim.width + this->text_ofs_x, ScaleGUITrad(500)),
(uint)data.size() * this->line_height - padding (uint)data.size() * this->line_height - padding
}; };
} }
std::pair<Point, Rect> GetPositions(Rect rect, int row, SpriteID icon) { std::pair<Point, Rect> GetPositions(Rect rect, int row, uint indent, SpriteID icon) {
auto icon_height = this->max_height; auto icon_height = this->max_height;
auto ofs_x = 0; auto ofs_x = 0;
int ind = WidgetDimensions::scaled.hsep_indent * indent;
if (icon != PAL_NONE) { if (icon != PAL_NONE) {
icon_height = GetSpriteSize(icon).height; icon_height = GetSpriteSize(icon).height;
ofs_x = this->text_ofs_x; ofs_x = this->text_ofs_x;
} }
return { return {
{ {
rect.left, rect.left + ind,
rect.top + row * this->line_height + (this->max_height - icon_height) / 2, rect.top + row * this->line_height + (this->max_height - icon_height) / 2,
}, },
{ {
rect.left + ofs_x, rect.left + ofs_x + ind,
rect.top + row * this->line_height + this->text_ofs_y, rect.top + row * this->line_height + this->text_ofs_y,
rect.right, rect.right,
rect.bottom rect.bottom
@@ -227,6 +229,11 @@ public:
~BuildInfoOverlay() override {} ~BuildInfoOverlay() override {}
void Show(int x, int y, BuildInfoOverlayData data) { void Show(int x, int y, BuildInfoOverlayData data) {
if (data.size() == 0) {
this->visible = false;
return;
}
if (this->visible && this->x == x && this->y == y && this->data == data) return;
this->x = x; this->x = x;
this->y = y; this->y = y;
this->data = data; this->data = data;
@@ -248,9 +255,8 @@ public:
void DrawContent(Rect rect) override { void DrawContent(Rect rect) override {
int row = 0; int row = 0;
for (const auto &[icon, s] : this->data) { for (const auto &[indent, icon, s] : this->data) {
auto [ipos, srect] = this->aligner.GetPositions(rect, row, icon); auto [ipos, srect] = this->aligner.GetPositions(rect, row, indent, icon);
DrawString(srect.left, srect.right, srect.top, s); DrawString(srect.left, srect.right, srect.top, s);
if (icon != PAL_NONE) if (icon != PAL_NONE)
DrawSprite(icon, PAL_NONE, ipos.x, ipos.y); DrawSprite(icon, PAL_NONE, ipos.x, ipos.y);

View File

@@ -6,7 +6,7 @@
namespace citymania { namespace citymania {
typedef std::vector<std::pair<SpriteID, std::string>> BuildInfoOverlayData; typedef std::vector<std::tuple<uint, SpriteID, std::string>> BuildInfoOverlayData;
void UndrawOverlays(int left, int top, int right, int bottom); void UndrawOverlays(int left, int top, int right, int bottom);
void DrawOverlays(); void DrawOverlays();

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
#ifndef CM_STATION_GUI_HPP #ifndef CM_STATION_GUI_HPP
#define CM_STATION_GUI_HPP #define CM_STATION_GUI_HPP
#include "cm_command_type.hpp"
#include "cm_highlight_type.hpp" #include "cm_highlight_type.hpp"
#include "../core/geometry_type.hpp" #include "../core/geometry_type.hpp"
@@ -9,6 +10,9 @@
#include "../station_gui.h" #include "../station_gui.h"
#include "../station_type.h" #include "../station_type.h"
#include <concepts>
#include <optional>
namespace citymania { namespace citymania {
const DiagDirection DEPOTDIR_AUTO = DIAGDIR_END; const DiagDirection DEPOTDIR_AUTO = DIAGDIR_END;
@@ -26,29 +30,15 @@ struct RailStationGUISettings {
uint16_t station_count; ///< Number of custom stations (if newstations is \c true ) uint16_t station_count; ///< Number of custom stations (if newstations is \c true )
}; };
enum class StationBuildingStatus {
IMPOSSIBLE = 0,
QUERY = 1,
JOIN = 2,
NEW = 3,
};
// void SetStationBiildingStatus(StationBuildingStatus status);
// void SetStationTileSelectSize(int w, int h, int catchment); // void SetStationTileSelectSize(int w, int h, int catchment);
bool UseImprovedStationJoin(); bool UseImprovedStationJoin();
void OnStationTileSetChange(const Station *station, bool adding, StationType type); void OnStationTileSetChange(const Station *station, bool adding, StationType type);
void OnStationPartBuilt(const Station *station); void OnStationPartBuilt(const Station *station);
void OnStationRemoved(const Station *station); void OnStationRemoved(const Station *station);
void PlaceRoadStop(TileIndex start_tile, TileIndex end_tile, RoadStopType stop_type, bool adjacent, RoadType rt, StringID err_msg);
void HandleStationPlacement(TileIndex start, TileIndex end);
void PlaceRail_Station(TileIndex tile);
void PlaceDock(TileIndex tile, TileIndex tile_to);
void PlaceAirport(TileIndex tile);
void SelectStationToJoin(const Station *station); // void SelectStationToJoin(const Station *station);
// const Station *GetStationToJoin(); // const Station *GetStationToJoin();
void MarkCoverageHighlightDirty(); void MarkCoverageHighlightDirty();
bool CheckRedrawStationCoverage();
void AbortStationPlacement(); void AbortStationPlacement();
std::optional<std::string> GetStationCoverageAreaText(TileIndex tile, int w, int h, int rad, StationCoverageType sct, bool supplies); std::optional<std::string> GetStationCoverageAreaText(TileIndex tile, int w, int h, int rad, StationCoverageType sct, bool supplies);
@@ -58,6 +48,7 @@ DiagDirection AutodetectRoadObjectDirection(TileIndex tile, Point pt, RoadType r
DiagDirection AutodetectDriveThroughRoadStopDirection(TileArea area, Point pt, RoadType roadtype); DiagDirection AutodetectDriveThroughRoadStopDirection(TileArea area, Point pt, RoadType roadtype);
DiagDirection AutodetectRailObjectDirection(TileIndex tile, Point pt); DiagDirection AutodetectRailObjectDirection(TileIndex tile, Point pt);
void SetSelectedStationToJoin(StationID station_id); void SetSelectedStationToJoin(StationID station_id);
void ResetJoinStationHighlight();
struct OverlayParams { struct OverlayParams {
@@ -66,144 +57,305 @@ struct OverlayParams {
StationCoverageType coverage_type; StationCoverageType coverage_type;
}; };
// Remove action classes
class PreviewStationType { class RemoveHandler {
public: public:
virtual ~RemoveHandler() = default;
virtual up<Command> GetCommand(TileArea area) = 0;
virtual bool Execute(TileArea area) = 0;
};
template<typename Handler>
concept ImplementsRemoveHandler = std::derived_from<Handler, RemoveHandler>;
template<ImplementsRemoveHandler Handler>
class RemoveAction : public Action {
private:
Handler handler;
TileIndex start_tile = INVALID_TILE; TileIndex start_tile = INVALID_TILE;
TileIndex cur_tile = INVALID_TILE; TileIndex cur_tile = INVALID_TILE;
virtual ~PreviewStationType() {};
TileIndex GetStartTile() const { return start_tile == INVALID_TILE ? cur_tile : start_tile; }
virtual bool IsDragDrop() const { return true; };
virtual CursorID GetCursor() const =0;
virtual TileArea GetArea(bool remove_mode) const =0;
virtual void Update(Point /* pt */, TileIndex /* tile */) {};
virtual up<Command> GetCommand(bool adjacent, StationID join_to) const =0;
virtual up<Command> GetRemoveCommand() const =0;
virtual void AddPreviewTiles(HighlightMap &hlmap, SpriteID palette) const =0;
virtual bool Execute(up<Command> cmd, bool remove_mode) const =0;
virtual OverlayParams GetOverlayParams() const =0;
};
class RailStationPreview : public PreviewStationType {
public: public:
virtual ~RailStationPreview() {}; RemoveAction(const Handler &handler) : handler{handler} {}
~RemoveAction() override = default;
// RailPreviewStation(RailStationGUISettings &settings) :settings{settings} {}
bool IsDragDrop() const override;
CursorID GetCursor() const override;
TileArea GetArea(bool remove_mode) const override;
up<Command> GetCommand(bool adjacent, StationID join_to) const override;
up<Command> GetRemoveCommand() const override;
void AddPreviewTiles(HighlightMap &hlmap, SpriteID palette) const override;
bool Execute(up<Command> cmd, bool remove_mode) const override;
OverlayParams GetOverlayParams() const override;
};
class RoadStationPreview : public PreviewStationType {
protected:
DiagDirection ddir;
RoadStopType stop_type;
public:
RoadStationPreview(RoadStopType stop_type) :stop_type{stop_type} {}
virtual ~RoadStationPreview() {};
bool IsDragDrop() const override;
CursorID GetCursor() const override;
TileArea GetArea(bool remove_mode) const override;
void Update(Point pt, TileIndex tile) override; void Update(Point pt, TileIndex tile) override;
up<Command> GetCommand(bool adjacent, StationID join_to) const override; std::optional<TileArea> GetArea() const override;
up<Command> GetRemoveCommand() const override; bool HandleMousePress() override;
void AddPreviewTiles(HighlightMap &hlmap, SpriteID palette) const override; void HandleMouseRelease() override;
bool Execute(up<Command> cmd, bool remove_mode) const override; ToolGUIInfo GetGUIInfo() override;
OverlayParams GetOverlayParams() const override; void OnStationRemoved(const Station *) override;
}; };
class DockPreview : public PreviewStationType {
protected:
DiagDirection ddir;
// StationSelect classes
class StationSelectHandler {
public: public:
DockPreview() {} virtual ~StationSelectHandler() = default;
virtual ~DockPreview() {}; virtual void SelectStationToJoin(StationID station_id) = 0;
bool IsDragDrop() const override;
CursorID GetCursor() const override;
TileArea GetArea(bool remove_mode) const override;
void Update(Point pt, TileIndex tile) override;
up<Command> GetCommand(bool adjacent, StationID join_to) const override;
up<Command> GetRemoveCommand() const override;
void AddPreviewTiles(HighlightMap &hlmap, SpriteID palette) const override;
bool Execute(up<Command> cmd, bool remove_mode) const override;
OverlayParams GetOverlayParams() const override;
}; };
template<typename Handler>
concept ImplementsStationSelectHandler = std::derived_from<Handler, StationSelectHandler>;
class StationPreviewBase : public Preview { template<ImplementsStationSelectHandler Handler>
protected: class StationSelectAction : public Action {
sp<PreviewStationType> type; private:
bool remove_mode = false; Handler handler;
bool keep_rail = true; // whether to keep rail in remove mode TileIndex cur_tile = INVALID_TILE;
StationID station_to_join = INVALID_STATION; StationID selected_station = INVALID_STATION;
bool adjacent_stations = false;
bool show_coverage = true;
void AddAreaTiles(HighlightMap &hlmap, bool add_current, bool show_join_area);
virtual void Execute() = 0;
up<Command> GetCommand(bool adjacent, StationID join_to);
void AddStationPreview(HighlightMap &hlmap, SpriteID palette);
public: public:
StationPreviewBase(sp<PreviewStationType> type) :type{type} {}; StationSelectAction(const Handler &handler) : handler{handler} {}
CursorID GetCursor() override { return this->type->GetCursor(); }; ~StationSelectAction() override = default;
void Update(Point pt, TileIndex tile) override; void Update(Point pt, TileIndex tile) override;
bool HandleMousePress() override; bool HandleMousePress() override;
void HandleMouseRelease() override; void HandleMouseRelease() override;
std::optional<std::string> GetStationCoverageAreaText(int rad, StationCoverageType sct, bool supplies); ToolGUIInfo GetGUIInfo() override;
std::vector<std::pair<SpriteID, std::string>> GetOverlayData();
};
class VanillaStationPreview : public StationPreviewBase {
protected:
SpriteID palette;
void Execute() override;
public:
StationID selected_station_to_join = INVALID_STATION;
VanillaStationPreview(sp<PreviewStationType> type) :StationPreviewBase{type} {};
virtual ~VanillaStationPreview() {};
void Update(Point pt, TileIndex tile) override;
HighlightMap GetHighlightMap() override;
void OnStationRemoved(const Station *station) override; void OnStationRemoved(const Station *station) override;
}; };
// PlacementAction
class StationPreview : public StationPreviewBase { class PlacementAction : public Action {
protected:
bool select_mode = false;
void Execute() override;
up<Command> GetCommand();
public: public:
StationPreview(sp<PreviewStationType> type); ~PlacementAction() override = default;
virtual ~StationPreview(); ToolGUIInfo PrepareGUIInfo(std::optional<ObjectHighlight> ohl, up<Command> cmd, StationCoverageType sct, uint rad);
};
// SizedPlacement classes
class SizedPlacementHandler {
public:
virtual ~SizedPlacementHandler() = default;
virtual up<Command> GetCommand(TileIndex tile, StationID to_join) = 0;
virtual bool Execute(TileIndex tile) = 0;
virtual std::optional<ObjectHighlight> GetObjectHighlight(TileIndex tile) = 0;
virtual std::pair<StationCoverageType, uint> GetCatchmentParams() = 0;
};
template<typename Handler>
concept ImplementsSizedPlacementHandler = std::derived_from<Handler, SizedPlacementHandler>;
template<ImplementsSizedPlacementHandler Handler>
class SizedPlacementAction : public PlacementAction {
private:
Handler handler;
TileIndex cur_tile = INVALID_TILE;
public:
SizedPlacementAction(const Handler &handler) : handler{handler} {}
~SizedPlacementAction() override = default;
void Update(Point pt, TileIndex tile) override; void Update(Point pt, TileIndex tile) override;
std::optional<TileArea> GetArea() const override { return std::nullopt; };
bool HandleMousePress() override; bool HandleMousePress() override;
void HandleMouseRelease() override;
HighlightMap GetHighlightMap() override; ToolGUIInfo GetGUIInfo() override;
void OnStationRemoved(const Station *station) override; void OnStationRemoved(const Station *) override;
}; };
// SPR_CURSOR_BUS_STATION SPR_CURSOR_TRUCK_STATION // DragNDropPlacement classes
class DragNDropPlacementHandler {
public:
virtual ~DragNDropPlacementHandler() = default;
virtual up<Command> GetCommand(TileArea area, StationID to_join) = 0;
virtual bool Execute(TileArea area) = 0;
virtual std::optional<ObjectHighlight> GetObjectHighlight(TileArea area) = 0;
virtual std::pair<StationCoverageType, uint> GetCatchmentParams() = 0;
};
template<typename Handler>
concept ImplementsDragNDropPlacementHandler = std::derived_from<Handler, DragNDropPlacementHandler>;
bool HandleStationPlacePushButton(Window *w, WidgetID widget, sp<PreviewStationType> type); template<ImplementsDragNDropPlacementHandler Handler>
class DragNDropPlacementAction : public PlacementAction {
private:
TileIndex start_tile = INVALID_TILE;
TileIndex cur_tile = INVALID_TILE;
Handler handler;
public:
DragNDropPlacementAction(const Handler &handler) :handler{handler} {};
~DragNDropPlacementAction() override = default;
void Update(Point pt, TileIndex tile) override;
std::optional<TileArea> GetArea() const override;
bool HandleMousePress() override;
void HandleMouseRelease() override;
ToolGUIInfo GetGUIInfo() override;
void OnStationRemoved(const Station *) override;
};
class StationBuildTool : public Tool {
public:
static StationID station_to_join;
static StationID current_selected_station;
static std::optional<ObjectHighlight> active_highlight;
class StationSelectHandler : public citymania::StationSelectHandler {
public:
StationBuildTool &tool;
StationSelectHandler(StationBuildTool &tool) : tool(tool) {}
~StationSelectHandler() {}
void SelectStationToJoin(StationID station_id) override { this->tool.SelectStationToJoin(station_id); };
};
~StationBuildTool() override = default;
void SelectStationToJoin(StationID station_id) { StationBuildTool::station_to_join = station_id; };
ToolGUIInfo GetGUIInfo() override {
if (!this->action) return {};
return this->action->GetGUIInfo();
}
void OnStationRemoved(const Station *station) override {
if (this->action) this->action->OnStationRemoved(station);
}
protected:
template<typename Thandler, typename Tcallback, typename Targ>
bool ExecuteBuildCommand(Thandler *handler, Tcallback callback, Targ arg);
};
// RailStationBuildTool
class RailStationBuildTool : public StationBuildTool {
private:
class RemoveHandler : public citymania::RemoveHandler {
public:
RailStationBuildTool &tool;
RemoveHandler(RailStationBuildTool &tool) : tool(tool) {}
~RemoveHandler() override = default;
up<Command> GetCommand(TileArea area) override;
bool Execute(TileArea area) override;
};
class SizedPlacementHandler : public citymania::SizedPlacementHandler {
public:
RailStationBuildTool &tool;
SizedPlacementHandler(RailStationBuildTool &tool) : tool(tool) {}
~SizedPlacementHandler() override = default;
up<Command> GetCommand(TileIndex tile, StationID to_join) override;
bool Execute(TileIndex tile) override;
std::optional<ObjectHighlight> GetObjectHighlight(TileIndex tile) override;
std::pair<StationCoverageType, uint> GetCatchmentParams() override { return {this->tool.GetCatchmentParams()}; };
};
class DragNDropPlacementHandler: public citymania::DragNDropPlacementHandler {
public:
RailStationBuildTool &tool;
DragNDropPlacementHandler(RailStationBuildTool &tool) :tool{tool} {}
~DragNDropPlacementHandler() override = default;
up<Command> GetCommand(TileArea area, StationID to_join) override;
bool Execute(TileArea area) override;
std::optional<ObjectHighlight> GetObjectHighlight(TileArea area) override;
std::pair<StationCoverageType, uint> GetCatchmentParams() override { return {this->tool.GetCatchmentParams()}; };
};
std::optional<ObjectHighlight> GetStationObjectHighlight(TileIndex start_tile, TileIndex end_tile) const;
std::pair<StationCoverageType, uint> GetCatchmentParams() { return {SCT_ALL, CA_TRAIN}; };
public:
RailStationBuildTool();
~RailStationBuildTool() override = default;
void Update(Point pt, TileIndex tile) override;
CursorID GetCursor() override;
private:
enum class Mode { REMOVE, SELECT, DRAGDROP, SIZED };
Mode mode;
};
// RoadStopBuildTool
class RoadStopBuildTool : public StationBuildTool {
private:
class RemoveHandler : public citymania::RemoveHandler {
public:
RoadStopBuildTool &tool;
RemoveHandler(RoadStopBuildTool &tool) : tool(tool) {}
~RemoveHandler() override = default;
up<Command> GetCommand(TileArea area) override;
bool Execute(TileArea area) override;
};
class DragNDropPlacementHandler: public citymania::DragNDropPlacementHandler {
public:
RoadStopBuildTool &tool;
DragNDropPlacementHandler(RoadStopBuildTool &tool) :tool{tool} {}
~DragNDropPlacementHandler() override = default;
up<Command> GetCommand(TileArea area, StationID to_join) override;
bool Execute(TileArea area) override;
std::optional<ObjectHighlight> GetObjectHighlight(TileArea area) override;
std::pair<StationCoverageType, uint> GetCatchmentParams() override { return this->tool.GetCatchmentParams(); };
};
std::pair<StationCoverageType, uint> GetCatchmentParams() {
if (this->stop_type == ROADSTOP_BUS) return {SCT_PASSENGERS_ONLY, CA_BUS};
else return {SCT_NON_PASSENGERS_ONLY, CA_TRUCK};
};
public:
RoadStopBuildTool(RoadStopType stop_type);
~RoadStopBuildTool() override = default;
void Update(Point pt, TileIndex tile) override;
CursorID GetCursor() override;
private:
enum class Mode { REMOVE, SELECT, DRAGDROP };
Mode mode;
RoadStopType stop_type;
DiagDirection ddir = DIAGDIR_NE;
};
// --- DockBuildTool ---
class DockBuildTool : public StationBuildTool {
private:
class RemoveHandler : public citymania::RemoveHandler {
public:
DockBuildTool &tool;
RemoveHandler(DockBuildTool &tool) : tool(tool) {}
~RemoveHandler() override = default;
up<Command> GetCommand(TileArea area) override;
bool Execute(TileArea area) override;
};
class SizedPlacementHandler : public citymania::SizedPlacementHandler {
public:
DockBuildTool &tool;
SizedPlacementHandler(DockBuildTool &tool) : tool(tool) {}
~SizedPlacementHandler() override = default;
up<Command> GetCommand(TileIndex tile, StationID to_join) override;
bool Execute(TileIndex tile) override;
std::optional<ObjectHighlight> GetObjectHighlight(TileIndex tile) override;
std::pair<StationCoverageType, uint> GetCatchmentParams() override { return {SCT_ALL, CA_DOCK}; };
};
public:
DockBuildTool();
~DockBuildTool() override = default;
void Update(Point pt, TileIndex tile) override;
CursorID GetCursor() override;
private:
enum class Mode { REMOVE, SELECT, SIZED };
Mode mode;
DiagDirection ddir;
};
// --- AirportBuildTool ---
class AirportBuildTool : public StationBuildTool {
private:
class RemoveHandler : public citymania::RemoveHandler {
public:
AirportBuildTool &tool;
RemoveHandler(AirportBuildTool &tool) : tool(tool) {}
~RemoveHandler() override = default;
up<Command> GetCommand(TileArea area) override;
bool Execute(TileArea area) override;
};
class SizedPlacementHandler : public citymania::SizedPlacementHandler {
public:
AirportBuildTool &tool;
SizedPlacementHandler(AirportBuildTool &tool) : tool(tool) {}
~SizedPlacementHandler() override = default;
up<Command> GetCommand(TileIndex tile, StationID to_join) override;
bool Execute(TileIndex tile) override;
std::optional<ObjectHighlight> GetObjectHighlight(TileIndex tile) override;
std::pair<StationCoverageType, uint> GetCatchmentParams() override;
};
public:
AirportBuildTool();
~AirportBuildTool() override = default;
void Update(Point pt, TileIndex tile) override;
CursorID GetCursor() override;
private:
enum class Mode { REMOVE, SELECT, SIZED };
Mode mode;
};
ToolGUIInfo GetSelectedStationGUIInfo();
} // namespace citymania } // namespace citymania

View File

@@ -6,6 +6,7 @@
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
#include <sstream>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
@@ -72,6 +73,56 @@ enum class ControllerType: uint8_t {
TOWN_DEFENCE = 6, TOWN_DEFENCE = 6,
}; };
// Some utility funcitons for strings
namespace string {
template<typename T>
static inline std::string join(T strings, std::string separator) {
// TODO add map function (can be used in ListGameModeCodes)?
std::ostringstream res;
bool first = true;
for (auto s: strings) {
if (!first)res << separator;
res << s;
first = false;
}
return res.str();
}
static inline void iltrim(std::string &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int c) {
return !std::isspace(c);
}));
}
static inline void irtrim(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(), [](int c) {
return !std::isspace(c);
}).base(), s.end());
}
static inline void itrim(std::string &s) {
iltrim(s);
irtrim(s);
}
static inline std::string ltrim(std::string s) {
iltrim(s);
return s;
}
static inline std::string rtrim(std::string s) {
irtrim(s);
return s;
}
static inline std::string trim(std::string s) {
itrim(s);
return s;
}
}; // namespace string
} // namespace citymania } // namespace citymania

View File

@@ -237,16 +237,14 @@ public:
Commands get_command() override; Commands get_command() override;
}; };
class BuildAirport: public Command { class BuildAirport: public StationBuildCommand {
public: public:
TileIndex tile; TileIndex tile;
byte airport_type; byte airport_type;
byte layout; byte layout;
StationID station_to_join;
bool adjacent;
BuildAirport(TileIndex tile, byte airport_type, byte layout, StationID station_to_join, bool adjacent) BuildAirport(TileIndex tile, byte airport_type, byte layout, StationID station_to_join, bool adjacent)
:tile{tile}, airport_type{airport_type}, layout{layout}, station_to_join{station_to_join}, adjacent{adjacent} {} :StationBuildCommand{station_to_join, adjacent}, tile{tile}, airport_type{airport_type}, layout{layout} {}
~BuildAirport() override {} ~BuildAirport() override {}
bool _post(::CommandCallback * callback) override; bool _post(::CommandCallback * callback) override;
@@ -254,14 +252,12 @@ public:
Commands get_command() override; Commands get_command() override;
}; };
class BuildDock: public Command { class BuildDock: public StationBuildCommand {
public: public:
TileIndex tile; TileIndex tile;
StationID station_to_join;
bool adjacent;
BuildDock(TileIndex tile, StationID station_to_join, bool adjacent) BuildDock(TileIndex tile, StationID station_to_join, bool adjacent)
:tile{tile}, station_to_join{station_to_join}, adjacent{adjacent} {} :StationBuildCommand{station_to_join, adjacent}, tile{tile} {}
~BuildDock() override {} ~BuildDock() override {}
bool _post(::CommandCallback * callback) override; bool _post(::CommandCallback * callback) override;
@@ -269,7 +265,7 @@ public:
Commands get_command() override; Commands get_command() override;
}; };
class BuildRailStation: public Command { class BuildRailStation: public StationBuildCommand {
public: public:
TileIndex tile_org; TileIndex tile_org;
RailType rt; RailType rt;
@@ -278,11 +274,9 @@ public:
byte plat_len; byte plat_len;
StationClassID spec_class; StationClassID spec_class;
uint16_t spec_index; uint16_t spec_index;
StationID station_to_join;
bool adjacent;
BuildRailStation(TileIndex tile_org, RailType rt, Axis axis, byte numtracks, byte plat_len, StationClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent) BuildRailStation(TileIndex tile_org, RailType rt, Axis axis, byte numtracks, byte plat_len, StationClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
:tile_org{tile_org}, rt{rt}, axis{axis}, numtracks{numtracks}, plat_len{plat_len}, spec_class{spec_class}, spec_index{spec_index}, station_to_join{station_to_join}, adjacent{adjacent} {} :StationBuildCommand{station_to_join, adjacent}, tile_org{tile_org}, rt{rt}, axis{axis}, numtracks{numtracks}, plat_len{plat_len}, spec_class{spec_class}, spec_index{spec_index} {}
~BuildRailStation() override {} ~BuildRailStation() override {}
bool _post(::CommandCallback * callback) override; bool _post(::CommandCallback * callback) override;
@@ -305,7 +299,7 @@ public:
Commands get_command() override; Commands get_command() override;
}; };
class BuildRoadStop: public Command { class BuildRoadStop: public StationBuildCommand {
public: public:
TileIndex tile; TileIndex tile;
uint8_t width; uint8_t width;
@@ -316,11 +310,9 @@ public:
RoadType rt; RoadType rt;
RoadStopClassID spec_class; RoadStopClassID spec_class;
uint16_t spec_index; uint16_t spec_index;
StationID station_to_join;
bool adjacent;
BuildRoadStop(TileIndex tile, uint8_t width, uint8_t length, RoadStopType stop_type, bool is_drive_through, DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent) BuildRoadStop(TileIndex tile, uint8_t width, uint8_t length, RoadStopType stop_type, bool is_drive_through, DiagDirection ddir, RoadType rt, RoadStopClassID spec_class, uint16_t spec_index, StationID station_to_join, bool adjacent)
:tile{tile}, width{width}, length{length}, stop_type{stop_type}, is_drive_through{is_drive_through}, ddir{ddir}, rt{rt}, spec_class{spec_class}, spec_index{spec_index}, station_to_join{station_to_join}, adjacent{adjacent} {} :StationBuildCommand{station_to_join, adjacent}, tile{tile}, width{width}, length{length}, stop_type{stop_type}, is_drive_through{is_drive_through}, ddir{ddir}, rt{rt}, spec_class{spec_class}, spec_index{spec_index} {}
~BuildRoadStop() override {} ~BuildRoadStop() override {}
bool _post(::CommandCallback * callback) override; bool _post(::CommandCallback * callback) override;

View File

@@ -38,10 +38,12 @@
#include "table/sprites.h" #include "table/sprites.h"
#include "table/strings.h" #include "table/strings.h"
#include "citymania/cm_highlight.hpp"
#include "citymania/cm_hotkeys.hpp" #include "citymania/cm_hotkeys.hpp"
#include "citymania/cm_station_gui.hpp" #include "citymania/cm_station_gui.hpp"
#include "safeguards.h" #include "safeguards.h"
#include <memory>
static void ShowBuildDockStationPicker(Window *parent); static void ShowBuildDockStationPicker(Window *parent);
static void ShowBuildDocksDepotPicker(Window *parent); static void ShowBuildDocksDepotPicker(Window *parent);
@@ -171,8 +173,8 @@ struct BuildDocksToolbarWindow : Window {
break; break;
case WID_DT_STATION: // Build station button case WID_DT_STATION: // Build station button
// if (HandlePlacePushButton(this, WID_DT_STATION, SPR_CURSOR_DOCK, HT_SPECIAL, CM_DDSP_BUILD_DOCK)) ShowBuildDockStationPicker(this); // if (HandlePlacePushButton(this, WID_DT_STATION, SPR_CURSOR_DOCK, HT_SPECIAL, CM_DDSP_BUILD_DOCK)) {
if (citymania::HandleStationPlacePushButton(this, WID_DT_STATION, std::make_shared<citymania::DockPreview>())) { if (citymania::HandlePlacePushButton(this, WID_DT_STATION, std::make_unique<citymania::DockBuildTool>())) {
ShowBuildDockStationPicker(this); ShowBuildDockStationPicker(this);
} }
@@ -218,14 +220,13 @@ struct BuildDocksToolbarWindow : Window {
break; break;
case WID_DT_STATION: { // Build station button case WID_DT_STATION: { // Build station button
NOT_REACHED(); // CityMania uses tools
/* Determine the watery part of the dock. */ /* Determine the watery part of the dock. */
/*
DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile)); DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile));
TileIndex tile_to = (dir != INVALID_DIAGDIR ? TileAddByDiagDir(tile, ReverseDiagDir(dir)) : tile); TileIndex tile_to = (dir != INVALID_DIAGDIR ? TileAddByDiagDir(tile, ReverseDiagDir(dir)) : tile);
if (citymania::UseImprovedStationJoin()) {
citymania::PlaceDock(tile, tile_to);
break;
}
bool adjacent = citymania::_fn_mod; bool adjacent = citymania::_fn_mod;
auto proc = [=](bool test, StationID to_join) -> bool { auto proc = [=](bool test, StationID to_join) -> bool {
@@ -237,7 +238,7 @@ struct BuildDocksToolbarWindow : Window {
}; };
ShowSelectStationIfNeeded(TileArea(tile, tile_to), proc); ShowSelectStationIfNeeded(TileArea(tile, tile_to), proc);
break; break; */
} }
case WID_DT_BUOY: // Build buoy button case WID_DT_BUOY: // Build buoy button
@@ -290,7 +291,7 @@ struct BuildDocksToolbarWindow : Window {
CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_WATER); CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_WATER);
CloseWindowById(WC_SELECT_STATION, 0); CloseWindowById(WC_SELECT_STATION, 0);
CloseWindowByClass(WC_BUILD_BRIDGE); CloseWindowByClass(WC_BUILD_BRIDGE);
citymania::AbortStationPlacement(); citymania::AbortStationPlacement();
} }

View File

@@ -6298,13 +6298,26 @@ CM_BUILDING_PREVIEW_COST_ENOUGH :Cost
CM_STR_NO_BLUEPRINT_IN_SLOT :{WHITE}No blueprint in slot {NUM} CM_STR_NO_BLUEPRINT_IN_SLOT :{WHITE}No blueprint in slot {NUM}
CM_STR_ABOUT_MENU_LOGIN_WINDOW :CityMania server login CM_STR_ABOUT_MENU_LOGIN_WINDOW :CityMania server login
CM_STR_BUILD_INFO_OVERLAY_COST_OK :{WHITE}Cost: {CURRENCY_LONG} CM_STR_BUILD_INFO_OVERLAY_COST_OK :{GREEN}Cost: {CURRENCY_LONG}
CM_STR_BUILD_INFO_OVERLAY_COST_NO_MONEY :{RED}Cost: {CURRENCY_LONG} CM_STR_BUILD_INFO_OVERLAY_COST_NO_MONEY :{RED}Cost: {CURRENCY_LONG}
CM_STR_BUILD_INFO_OVERLAY_STATION_ACCEPTS :{WHITE}Accepts: CM_STR_BULID_INFO_OVERLAY_ACCEPTS_CARGO_PARTIAL :{SILVER}{CARGO_LIST}({NUM}/8)
CM_STR_BULID_INFO_OVERLAY_ACCEPTS_CARGO_FULL :{GOLD}{CARGO_LIST}({NUM}/8)
CM_STR_BULID_INFO_OVERLAY_ACCEPTS_CARGO :{GOLD}{CARGO_LIST}
CM_STR_BULID_INFO_OVERLAY_ACCEPTS :{WHITE}Accepts: {GOLD}{RAW_STRING}
CM_STR_BUILD_INFO_OVERLAY_STATION_SUPPLIES :{WHITE}Supplies: CM_STR_BUILD_INFO_OVERLAY_STATION_SUPPLIES :{WHITE}Supplies:
CM_STR_BUILD_INFO_OVERLAY_STATION_CARGO :{WHITE}{CARGO_LONG} CM_STR_BUILD_INFO_OVERLAY_STATION_CARGO :{GOLD}{CARGO_LONG}
CM_STR_BUILD_INFO_OVERLAY_ERROR :{RED}{STRING} CM_STR_BUILD_INFO_OVERLAY_ERROR :{RED}Error: {STRING}
CM_STR_BUILD_INFO_OVERLAY_ERROR_UNKNOWN :{RED}Unknown Error! CM_STR_BUILD_INFO_OVERLAY_ERROR_UNKNOWN :{RED}Unknown Error!
CM_STR_BULID_INFO_OVERLAY_JOIN_STATION :{LTBLUE}Join {STATION}
CM_STR_BULID_INFO_OVERLAY_NEW_STATION :{GOLD}New station
CM_STR_BULID_INFO_OVERLAY_TOWN_NONE :{WHITE}Town: {GOLD}None
CM_STR_BULID_INFO_OVERLAY_TOWN_ALLOWS :{WHITE}Town: {GREEN}{TOWN}[{NUM}], {STRING}
CM_STR_BULID_INFO_OVERLAY_TOWN_DENIES :{WHITE}Town: {RED}{TOWN}[{NUM}], {STRING}
CM_STR_BULID_INFO_OVERLAY_TOWN_NO_ADS :{WHITE}no ad zone
CM_STR_BULID_INFO_OVERLAY_TOWN_L_ADS :{GOLD}large {WHITE}ad zone
CM_STR_BULID_INFO_OVERLAY_TOWN_M_ADS :{GOLD}medium {WHITE}ad zone
CM_STR_BULID_INFO_OVERLAY_TOWN_S_ADS :{GOLD}small {WHITE}ad zone
CM_STR_BULID_INFO_OVERLAY_STATION_SIZE :{WHITE}Size: {GOLD}{NUM}x{NUM}
CM_STR_VEHICLE_INFO_BUILT_VALUE_WITH_ID :{LTBLUE}{ENGINE} {BLACK}Built: {LTBLUE}{NUM}{BLACK} Value: {LTBLUE}{CURRENCY_LONG}{BLACK} ID: {LTBLUE}{NUM} CM_STR_VEHICLE_INFO_BUILT_VALUE_WITH_ID :{LTBLUE}{ENGINE} {BLACK}Built: {LTBLUE}{NUM}{BLACK} Value: {LTBLUE}{CURRENCY_LONG}{BLACK} ID: {LTBLUE}{NUM}
@@ -6316,3 +6329,4 @@ CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_TRAIN :{BLACK}Show
CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_ROAD_VEHICLE :{BLACK}Show hidden ({NUM}) CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_ROAD_VEHICLE :{BLACK}Show hidden ({NUM})
CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_SHIP :{BLACK}Show hidden ({NUM}) CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_SHIP :{BLACK}Show hidden ({NUM})
CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_AIRCRAFT :{BLACK}Show hidden ({NUM}) CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_AIRCRAFT :{BLACK}Show hidden ({NUM})

View File

@@ -81,13 +81,6 @@ bool HandlePlacePushButton(Window *w, WidgetID widget, CursorID cursor, HighLigh
} }
SetObjectToPlace(cursor, PAL_NONE, mode, w->window_class, w->window_number, cm_process); SetObjectToPlace(cursor, PAL_NONE, mode, w->window_class, w->window_number, cm_process);
// if (cm_process == DDSP_BUILD_STATION) {
// if (citymania::UseImprovedStationJoin()) {
// citymania::SetActivePreview(std::make_unique<citymania::StationPreview>());
// } else {
// citymania::SetActivePreview(std::make_unique<citymania::VanillaStationPreview>());
// }
// }
w->LowerWidget(widget); w->LowerWidget(widget);
return true; return true;
} }

View File

@@ -202,6 +202,7 @@ void CcStation(Commands, const CommandCost &result, TileIndex tile)
*/ */
static void PlaceRail_Station(TileIndex tile) static void PlaceRail_Station(TileIndex tile)
{ {
NOT_REACHED(); // CityMania uses tools
if (_remove_button_clicked) { if (_remove_button_clicked) {
VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_STATION); VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_STATION);
VpSetPlaceSizingLimit(-1); VpSetPlaceSizingLimit(-1);
@@ -209,10 +210,6 @@ static void PlaceRail_Station(TileIndex tile)
VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_STATION); VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_STATION);
VpSetPlaceSizingLimit(_settings_game.station.station_spread); VpSetPlaceSizingLimit(_settings_game.station.station_spread);
} else { } else {
if (citymania::UseImprovedStationJoin()) {
citymania::PlaceRail_Station(tile);
return;
}
int w = _settings_client.gui.station_numtracks; int w = _settings_client.gui.station_numtracks;
int h = _settings_client.gui.station_platlength; int h = _settings_client.gui.station_platlength;
if (!_railstation.orientation) Swap(w, h); if (!_railstation.orientation) Swap(w, h);
@@ -680,13 +677,13 @@ struct BuildRailToolbarWindow : Window {
if (was_open) ResetObjectToPlace(); if (was_open) ResetObjectToPlace();
if (!was_open || dragdrop != _settings_client.gui.station_dragdrop) { if (!was_open || dragdrop != _settings_client.gui.station_dragdrop) {
_settings_client.gui.station_dragdrop = dragdrop; _settings_client.gui.station_dragdrop = dragdrop;
if (citymania::HandleStationPlacePushButton(this, WID_RAT_BUILD_STATION, std::make_shared<citymania::RailStationPreview>())) if (citymania::HandlePlacePushButton(this, WID_RAT_BUILD_STATION, std::make_unique<citymania::RailStationBuildTool>()))
// if (HandlePlacePushButton(this, WID_RAT_BUILD_STATION, SPR_CURSOR_BRIDGE, HT_RECT, DDSP_BUILD_STATION)) // if (HandlePlacePushButton(this, WID_RAT_BUILD_STATION, SPR_CURSOR_BRIDGE, HT_RECT, DDSP_BUILD_STATION))
ShowStationBuilder(this); ShowStationBuilder(this);
} }
this->last_user_action = WID_RAT_BUILD_STATION; this->last_user_action = WID_RAT_BUILD_STATION;
} else { /* button */ } else { /* button */
if (citymania::HandleStationPlacePushButton(this, WID_RAT_BUILD_STATION, std::make_shared<citymania::RailStationPreview>())) { if (citymania::HandlePlacePushButton(this, WID_RAT_BUILD_STATION, std::make_unique<citymania::RailStationBuildTool>())) {
// if (HandlePlacePushButton(this, WID_RAT_BUILD_STATION, SPR_CURSOR_BRIDGE, HT_RECT, DDSP_BUILD_STATION)) { // if (HandlePlacePushButton(this, WID_RAT_BUILD_STATION, SPR_CURSOR_BRIDGE, HT_RECT, DDSP_BUILD_STATION)) {
ShowStationBuilder(this); ShowStationBuilder(this);
this->last_user_action = WID_RAT_BUILD_STATION; this->last_user_action = WID_RAT_BUILD_STATION;
@@ -1043,7 +1040,7 @@ struct BuildRailToolbarWindow : Window {
Hotkey((uint16)0, "cm_blueprint_save_7", HOTKEY_BLUEPRINT_SAVE + 7), Hotkey((uint16)0, "cm_blueprint_save_7", HOTKEY_BLUEPRINT_SAVE + 7),
Hotkey((uint16)0, "cm_blueprint_save_8", HOTKEY_BLUEPRINT_SAVE + 8), Hotkey((uint16)0, "cm_blueprint_save_8", HOTKEY_BLUEPRINT_SAVE + 8),
Hotkey((uint16)0, "cm_blueprint_save_9", HOTKEY_BLUEPRINT_SAVE + 9), Hotkey((uint16)0, "cm_blueprint_save_9", HOTKEY_BLUEPRINT_SAVE + 9),
Hotkey(CM_WKC_MOUSE_MIDDLE, "cm_blueprint_rotate", HOTKEY_BLUEPRINT_ROTATE), Hotkey(CM_WKC_MOUSE_MIDDLE, "cm_blueprint_rotate", HOTKEY_BLUEPRINT_ROTATE),
}, RailToolbarGlobalHotkeys}; }, RailToolbarGlobalHotkeys};
}; };
@@ -1125,11 +1122,7 @@ Window *ShowBuildRailToolbar(RailType railtype)
static void HandleStationPlacement(TileIndex start, TileIndex end) static void HandleStationPlacement(TileIndex start, TileIndex end)
{ {
if (citymania::UseImprovedStationJoin()) { NOT_REACHED(); // CityMania uses tools
citymania::HandleStationPlacement(start, end);
return;
}
TileArea ta(start, end); TileArea ta(start, end);
uint numtracks = ta.w; uint numtracks = ta.w;
uint platlength = ta.h; uint platlength = ta.h;

View File

@@ -52,6 +52,7 @@
#include "citymania/cm_station_gui.hpp" #include "citymania/cm_station_gui.hpp"
#include "safeguards.h" #include "safeguards.h"
#include <memory>
static void ShowRVStationPicker(Window *parent, RoadStopType rs); static void ShowRVStationPicker(Window *parent, RoadStopType rs);
static void ShowRoadDepotPicker(Window *parent); static void ShowRoadDepotPicker(Window *parent);
@@ -220,11 +221,7 @@ void CcRoadStop(Commands, const CommandCost &result, TileIndex tile, uint8_t wid
*/ */
static void PlaceRoadStop(TileIndex start_tile, TileIndex end_tile, RoadStopType stop_type, bool adjacent, RoadType rt, StringID err_msg) static void PlaceRoadStop(TileIndex start_tile, TileIndex end_tile, RoadStopType stop_type, bool adjacent, RoadType rt, StringID err_msg)
{ {
if (citymania::UseImprovedStationJoin()) { NOT_REACHED(); // CityMania uses tools
citymania::PlaceRoadStop(start_tile, end_tile, stop_type, adjacent, rt, err_msg);
return;
}
TileArea ta(start_tile, end_tile); TileArea ta(start_tile, end_tile);
assert(_thd.cm.type == citymania::ObjectHighlight::Type::ROAD_STOP); assert(_thd.cm.type == citymania::ObjectHighlight::Type::ROAD_STOP);
// DiagDirection ddir = _roadstop_gui_settings.orientation; // DiagDirection ddir = _roadstop_gui_settings.orientation;
@@ -518,7 +515,8 @@ struct BuildRoadToolbarWindow : Window {
break; break;
case WID_ROT_BUS_STATION: { case WID_ROT_BUS_STATION: {
if (citymania::HandleStationPlacePushButton(this, WID_ROT_BUS_STATION, std::make_shared<citymania::RoadStationPreview>(ROADSTOP_BUS))) { if (citymania::HandlePlacePushButton(this, WID_ROT_BUS_STATION, std::make_unique<citymania::RoadStopBuildTool>(ROADSTOP_BUS))) {
// if (HandlePlacePushButton(this, WID_ROT_BUS_STATION, SPR_CURSOR_BUS_STATION, HT_RECT, DDSP_BUILD_BUSSTOP)) {
ShowRVStationPicker(this, ROADSTOP_BUS); ShowRVStationPicker(this, ROADSTOP_BUS);
this->last_started_action = widget; this->last_started_action = widget;
} }
@@ -526,7 +524,8 @@ struct BuildRoadToolbarWindow : Window {
} }
case WID_ROT_TRUCK_STATION: case WID_ROT_TRUCK_STATION:
if (citymania::HandleStationPlacePushButton(this, WID_ROT_TRUCK_STATION, std::make_shared<citymania::RoadStationPreview>(ROADSTOP_TRUCK))) { if (citymania::HandlePlacePushButton(this, WID_ROT_TRUCK_STATION, std::make_unique<citymania::RoadStopBuildTool>(ROADSTOP_TRUCK))) {
// if (HandlePlacePushButton(this, WID_ROT_TRUCK_STATION, SPR_CURSOR_TRUCK_STATION, HT_RECT, DDSP_BUILD_TRUCKSTOP)) {
ShowRVStationPicker(this, ROADSTOP_TRUCK); ShowRVStationPicker(this, ROADSTOP_TRUCK);
this->last_started_action = widget; this->last_started_action = widget;
} }
@@ -660,8 +659,8 @@ struct BuildRoadToolbarWindow : Window {
CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_ROAD); CloseWindowById(WC_BUILD_DEPOT, TRANSPORT_ROAD);
CloseWindowById(WC_SELECT_STATION, 0); CloseWindowById(WC_SELECT_STATION, 0);
CloseWindowByClass(WC_BUILD_BRIDGE); CloseWindowByClass(WC_BUILD_BRIDGE);
citymania::AbortStationPlacement(); citymania::AbortStationPlacement();
} }
void OnPlaceDrag(ViewportPlaceMethod select_method, [[maybe_unused]] ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt) override void OnPlaceDrag(ViewportPlaceMethod select_method, [[maybe_unused]] ViewportDragDropSelectionProcess select_proc, [[maybe_unused]] Point pt) override
@@ -1176,7 +1175,7 @@ static void ShowRoadDepotPicker(Window *parent)
/** Enum referring to the Hotkeys in the build road stop window */ /** Enum referring to the Hotkeys in the build road stop window */
enum BuildRoadStopHotkeys { enum BuildRoadStopHotkeys {
BROSHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string BROSHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string
CM_BROSHK_ROTATE, CM_BROSHK_ROTATE,
}; };
struct BuildRoadStationWindow : public PickerWindowBase { struct BuildRoadStationWindow : public PickerWindowBase {
@@ -1797,7 +1796,7 @@ static constexpr NWidgetPart _nested_road_station_picker_widgets[] = {
NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), NWidget(NWID_HORIZONTAL), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1),
NWidget(WWT_TEXTBTN, COLOUR_GREY, CM_WID_BROS_STATION_AUTO), SetMinimalSize(134, 12), SetDataTip(CM_STR_STATION_BUILD_ORIENTATION_AUTO, CM_STR_STATION_BUILD_ORIENTATION_AUTO_TOOLTIP), NWidget(WWT_TEXTBTN, COLOUR_GREY, CM_WID_BROS_STATION_AUTO), SetMinimalSize(134, 12), SetDataTip(CM_STR_STATION_BUILD_ORIENTATION_AUTO, CM_STR_STATION_BUILD_ORIENTATION_AUTO_TOOLTIP),
NWidget(WWT_TEXTBTN, COLOUR_GREY, CM_WID_BROS_STATION_XY_AUTO), SetMinimalSize(66, 12), SetDataTip(CM_STR_STATION_BUILD_ORIENTATION_AUTO, CM_STR_STATION_BUILD_ORIENTATION_AUTO_TOOLTIP), NWidget(WWT_TEXTBTN, COLOUR_GREY, CM_WID_BROS_STATION_XY_AUTO), SetMinimalSize(66, 12), SetDataTip(CM_STR_STATION_BUILD_ORIENTATION_AUTO, CM_STR_STATION_BUILD_ORIENTATION_AUTO_TOOLTIP),
EndContainer(), EndContainer(),
EndContainer(), EndContainer(),
NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_TYPE_SEL), NWidget(NWID_SELECTION, INVALID_COLOUR, WID_BROS_SHOW_NEWST_TYPE_SEL),
NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_BROS_SHOW_NEWST_TYPE), SetMinimalSize(144, 8), SetDataTip(STR_JUST_STRING, STR_NULL), SetTextStyle(TC_ORANGE), SetFill(1, 0), NWidget(WWT_LABEL, COLOUR_DARK_GREEN, WID_BROS_SHOW_NEWST_TYPE), SetMinimalSize(144, 8), SetDataTip(STR_JUST_STRING, STR_NULL), SetTextStyle(TC_ORANGE), SetFill(1, 0),

View File

@@ -59,13 +59,6 @@ int DrawStationCoverageAreaText(int left, int right, int top, StationCoverageTyp
{ {
TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y); TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y);
/* CityMania code begin */
auto s = citymania::GetStationCoverageAreaText(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad, sct, supplies);
if (s.has_value()) {
return DrawStringMultiLine(left, right, top, INT32_MAX, s.value());
}
/* CityMania code end */
CargoTypes cargo_mask = 0; CargoTypes cargo_mask = 0;
if (_thd.drawstyle == HT_RECT && tile < Map::Size()) { if (_thd.drawstyle == HT_RECT && tile < Map::Size()) {
CargoArray cargoes; CargoArray cargoes;
@@ -156,14 +149,9 @@ void FindStationsAroundSelection()
*/ */
void CheckRedrawStationCoverage(Window *w) void CheckRedrawStationCoverage(Window *w)
{ {
/* CityMania code begin */
if (citymania::UseImprovedStationJoin()) {
if (citymania::CheckRedrawStationCoverage()) w->SetDirty();
return;
}
/* CityMania code end */
/* Test if ctrl state changed */ /* Test if ctrl state changed */
/* CityMania uses tools and handles redraws differently
static bool _last_fn_pressed; static bool _last_fn_pressed;
if (citymania::_fn_mod != _last_fn_pressed) { if (citymania::_fn_mod != _last_fn_pressed) {
_thd.dirty = 0xff; _thd.dirty = 0xff;
@@ -178,6 +166,7 @@ void CheckRedrawStationCoverage(Window *w)
FindStationsAroundSelection<Station>(); FindStationsAroundSelection<Station>();
} }
} }
*/
} }
void CheckRedrawWaypointCoverage(const Window *) void CheckRedrawWaypointCoverage(const Window *)
@@ -2351,7 +2340,7 @@ struct SelectStationWindow : WindowPopup {
void Close([[maybe_unused]] int data = 0) override void Close([[maybe_unused]] int data = 0) override
{ {
if constexpr (std::is_same_v<T, Waypoint *>) SetViewportCatchmentSpecializedStation<T>(nullptr, true); if constexpr (std::is_same_v<T, Waypoint *>) SetViewportCatchmentSpecializedStation<T>(nullptr, true);
else citymania::SetSelectedStationToJoin(INVALID_STATION); else citymania::ResetJoinStationHighlight();
_thd.freeze = false; _thd.freeze = false;
this->Window::Close(); this->Window::Close();
@@ -2448,7 +2437,7 @@ struct SelectStationWindow : WindowPopup {
auto it = this->vscroll->GetScrolledItemFromWidget(_stations_nearby_list, pt.y, this, WID_JS_PANEL, WidgetDimensions::scaled.framerect.top); auto it = this->vscroll->GetScrolledItemFromWidget(_stations_nearby_list, pt.y, this, WID_JS_PANEL, WidgetDimensions::scaled.framerect.top);
const T *st = it == _stations_nearby_list.end() || *it == NEW_STATION ? nullptr : T::Get(*it); const T *st = it == _stations_nearby_list.end() || *it == NEW_STATION ? nullptr : T::Get(*it);
if constexpr (std::is_same_v<T, Waypoint>) SetViewportCatchmentSpecializedStation<T>(st, true); if constexpr (std::is_same_v<T, Waypoint>) SetViewportCatchmentSpecializedStation<T>(st, true);
else citymania::SetSelectedStationToJoin(*it); else citymania::SetSelectedStationToJoin(st ? st->index : INVALID_STATION);
} }
}; };

View File

@@ -11,6 +11,7 @@
#define TILEAREA_TYPE_H #define TILEAREA_TYPE_H
#include "map_func.h" #include "map_func.h"
#include "tile_type.h"
class OrthogonalTileIterator; class OrthogonalTileIterator;
class DiagonalTileIterator; class DiagonalTileIterator;
@@ -62,6 +63,12 @@ struct OrthogonalTileArea {
return TILE_ADDXY(this->tile, this->w / 2, this->h / 2); return TILE_ADDXY(this->tile, this->w / 2, this->h / 2);
} }
TileIndex CMGetEndTile() const
{
if (this->w == 0 || this->h == 0 || this->tile == INVALID_TILE) return INVALID_TILE;
return TILE_ADDXY(this->tile, this->w - 1, this->h - 1);
}
OrthogonalTileIterator begin() const; OrthogonalTileIterator begin() const;
OrthogonalTileIterator end() const; OrthogonalTileIterator end() const;

View File

@@ -243,7 +243,7 @@ static Point MapXYZToViewport(const Viewport *vp, int x, int y, int z)
} }
void DeleteWindowViewport(Window *w) void DeleteWindowViewport(Window *w)
{ {
delete w->viewport; delete w->viewport;
w->viewport = nullptr; w->viewport = nullptr;
} }
@@ -2916,7 +2916,7 @@ void UpdateTileSelection()
if ((new_drawstyle & HT_DRAG_MASK) != HT_NONE) SetSelectionTilesDirty(); if ((new_drawstyle & HT_DRAG_MASK) != HT_NONE) SetSelectionTilesDirty();
} }
citymania::UpdateActivePreview(); citymania::UpdateActiveTool();
} }
/** /**
@@ -3645,7 +3645,7 @@ static void CalcRaildirsDrawstyle(int x, int y, int method)
/* CityMania code start */ /* CityMania code start */
_thd.dir2 = HT_DIR_END; _thd.dir2 = HT_DIR_END;
ShowLengthMeasurement(b, TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y)); ShowLengthMeasurement(b, TileVirtXY(_thd.selstart.x, _thd.selstart.y), TileVirtXY(_thd.selend.x, _thd.selend.y));
/* CityMania code end */ /* CityMania code end */
} }
@@ -3826,7 +3826,7 @@ calc_heightdiff_single_direction:;
ShowMeasurementTooltips(measure_strings_length[index], index); ShowMeasurementTooltips(measure_strings_length[index], index);
} }
#endif #endif
/* With current code passing a HT_LINE style to calculate the height /* With current code passing a HT_LINE style to calculate the height
* difference is enough. However if/when a point-tool is created * difference is enough. However if/when a point-tool is created
* with this method, function should be called with new_style (below) * with this method, function should be called with new_style (below)
@@ -3995,7 +3995,7 @@ void SetObjectToPlaceWnd(CursorID icon, PaletteID pal, HighLightStyle mode, Wind
*/ */
void SetObjectToPlace(CursorID icon, PaletteID pal, HighLightStyle mode, WindowClass window_class, WindowNumber window_num, ViewportDragDropSelectionProcess cm_process) void SetObjectToPlace(CursorID icon, PaletteID pal, HighLightStyle mode, WindowClass window_class, WindowNumber window_num, ViewportDragDropSelectionProcess cm_process)
{ {
citymania::ResetActivePreview(); citymania::ResetActiveTool();
if (_thd.window_class != WC_INVALID) { if (_thd.window_class != WC_INVALID) {
/* Undo clicking on button and drag & drop */ /* Undo clicking on button and drag & drop */
Window *w = _thd.GetCallbackWnd(); Window *w = _thd.GetCallbackWnd();
@@ -4046,7 +4046,7 @@ void SetObjectToPlace(CursorID icon, PaletteID pal, HighLightStyle mode, WindowC
void ResetObjectToPlace() void ResetObjectToPlace()
{ {
SetObjectToPlace(SPR_CURSOR_MOUSE, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0, CM_DDSP_NONE); SetObjectToPlace(SPR_CURSOR_MOUSE, PAL_NONE, HT_NONE, WC_MAIN_WINDOW, 0, CM_DDSP_NONE);
citymania::ResetActivePreview(); citymania::ResetActiveTool();
} }
Point GetViewportStationMiddle(const Viewport *vp, const Station *st) Point GetViewportStationMiddle(const Viewport *vp, const Station *st)