Merge branch 'v14'
This commit is contained in:
@@ -24,6 +24,17 @@ FILES = [
|
||||
'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
|
||||
OUTPUT = BASE_DIR / 'src/citymania/generated/cm_gen_commands'
|
||||
GLOBAL_TYPES = set(('GoalType', 'GoalTypeID', 'GoalID'))
|
||||
@@ -245,13 +256,20 @@ def run():
|
||||
)
|
||||
for cmd in commands:
|
||||
name = cmd['name']
|
||||
args_list = ', '.join(f'{at} {an}' for at, an in cmd['args'])
|
||||
args_init = ', '.join(f'{an}{{{an}}}' for _, an in cmd['args'])
|
||||
base_class = BASE_CLASS.get(name, 'Command')
|
||||
base_fields = BASE_FIELDS.get(base_class, [])
|
||||
f.write(
|
||||
f'class {name}: public Command {{\n'
|
||||
f'class {name}: public {base_class} {{\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']:
|
||||
if an in base_fields:
|
||||
continue
|
||||
f.write(f' {at} {an};\n')
|
||||
f.write(f'\n')
|
||||
if args_init:
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "table/strings.h"
|
||||
|
||||
#include "citymania/cm_hotkeys.hpp"
|
||||
#include "citymania/cm_highlight.hpp"
|
||||
#include "citymania/cm_station_gui.hpp"
|
||||
|
||||
#include "safeguards.h"
|
||||
@@ -68,11 +69,7 @@ void CcBuildAirport(Commands, const CommandCost &result, TileIndex tile)
|
||||
*/
|
||||
static void PlaceAirport(TileIndex tile)
|
||||
{
|
||||
if (citymania::UseImprovedStationJoin()) {
|
||||
citymania::PlaceAirport(tile);
|
||||
return;
|
||||
}
|
||||
|
||||
NOT_REACHED(); // CityMania uses tools
|
||||
if (_selected_airport_index == -1) return;
|
||||
|
||||
uint8_t airport_type = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index)->GetIndex();
|
||||
@@ -133,7 +130,8 @@ struct BuildAirToolbarWindow : Window {
|
||||
{
|
||||
switch (widget) {
|
||||
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);
|
||||
this->last_user_action = widget;
|
||||
}
|
||||
|
||||
@@ -95,6 +95,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
|
||||
|
||||
#endif
|
||||
|
||||
@@ -184,7 +184,6 @@ struct TileZoning {
|
||||
static std::unique_ptr<TileZoning[]> _mz = nullptr;
|
||||
static IndustryType _industry_forbidden_tiles = IT_INVALID;
|
||||
|
||||
extern StationBuildingStatus _station_building_status;
|
||||
extern const Station *_station_to_join;
|
||||
extern const Station *_highlight_station_to_join;
|
||||
extern TileArea _highlight_join_area;
|
||||
@@ -399,7 +398,6 @@ bool ObjectTileHighlight::SetTileHighlight(TileHighlight &th, const TileInfo *)
|
||||
th.structure_pal = CM_PALETTE_HIDE_SPRITE;
|
||||
th.highlight_ground_pal = th.highlight_structure_pal = this->palette;
|
||||
return true;
|
||||
case ObjectTileHighlight::Type::BORDER:
|
||||
case ObjectTileHighlight::Type::TINT:
|
||||
th.ground_pal = th.structure_pal = this->palette;
|
||||
return true;
|
||||
@@ -506,6 +504,13 @@ ObjectHighlight ObjectHighlight::make_industry(TileIndex tile, IndustryType ind_
|
||||
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
|
||||
* @param tile Tile to use for adding the rail-track
|
||||
@@ -558,10 +563,10 @@ void ObjectHighlight::AddStationOverlayData(int w, int h, int rad, StationCovera
|
||||
if (cs == nullptr) continue;
|
||||
|
||||
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;
|
||||
}
|
||||
this->overlay_data.emplace_back(cs->GetCargoIcon(), GetString(CM_STR_BUILD_INFO_OVERLAY_STATION_CARGO, i, production[i] >> 8));
|
||||
this->overlay_data.emplace_back(1, cs->GetCargoIcon(), GetString(CM_STR_BUILD_INFO_OVERLAY_STATION_CARGO, i, production[i] >> 8));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -795,6 +800,20 @@ void ObjectHighlight::UpdateTiles() {
|
||||
}
|
||||
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:
|
||||
NOT_REACHED();
|
||||
}
|
||||
@@ -811,10 +830,11 @@ void ObjectHighlight::UpdateOverlay() {
|
||||
HideBuildInfoOverlay();
|
||||
return;
|
||||
}
|
||||
|
||||
auto err = this->cost.GetErrorMessage();
|
||||
// auto extra_err = this->cost.GetExtraErrorMessage();
|
||||
bool no_money = (err == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY);
|
||||
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->cost.GetCost()));
|
||||
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, this->cost.GetCost()));
|
||||
// if (this->cost.Failed() && err != STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) {
|
||||
// if (err == INVALID_STRING_ID) {
|
||||
// this->overlay_data.emplace_back(PAL_NONE, GetString(CM_STR_BUILD_INFO_OVERLAY_ERROR_UNKNOWN));
|
||||
@@ -861,6 +881,126 @@ void ObjectHighlight::MarkDirty() {
|
||||
}
|
||||
|
||||
|
||||
template <typename F>
|
||||
uint8 Get(uint32 x, uint32 y, F getter) {
|
||||
if (x >= Map::SizeX() || y >= Map::SizeY()) return 0;
|
||||
return getter(TileXY(x, y));
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
std::pair<ZoningBorder, uint8> CalcTileBorders(TileIndex tile, F getter) {
|
||||
auto x = TileX(tile), y = TileY(tile);
|
||||
ZoningBorder res = ZoningBorder::NONE;
|
||||
auto z = getter(tile);
|
||||
if (z == 0)
|
||||
return std::make_pair(res, 0);
|
||||
auto tr = Get(x - 1, y, getter);
|
||||
auto tl = Get(x, y - 1, getter);
|
||||
auto bl = Get(x + 1, y, getter);
|
||||
auto br = Get(x, y + 1, getter);
|
||||
if (tr < z) res |= ZoningBorder::TOP_RIGHT;
|
||||
if (tl < z) res |= ZoningBorder::TOP_LEFT;
|
||||
if (bl < z) res |= ZoningBorder::BOTTOM_LEFT;
|
||||
if (br < z) res |= ZoningBorder::BOTTOM_RIGHT;
|
||||
if (tr == z && tl == z && Get(x - 1, y - 1, getter) < z) res |= ZoningBorder::TOP_CORNER;
|
||||
if (tr == z && br == z && Get(x - 1, y + 1, getter) < z) res |= ZoningBorder::RIGHT_CORNER;
|
||||
if (br == z && bl == z && Get(x + 1, y + 1, getter) < z) res |= ZoningBorder::BOTTOM_CORNER;
|
||||
if (tl == z && bl == z && Get(x + 1, y - 1, getter) < z) res |= ZoningBorder::LEFT_CORNER;
|
||||
return std::make_pair(res, z);
|
||||
}
|
||||
|
||||
const HighlightMap::MapType &HighlightMap::GetMap() const {
|
||||
return this->map;
|
||||
}
|
||||
|
||||
void HighlightMap::Add(TileIndex tile, ObjectTileHighlight oth) {
|
||||
this->map[tile].push_back(oth);
|
||||
}
|
||||
|
||||
bool HighlightMap::Contains(TileIndex tile) const {
|
||||
return this->map.find(tile) != this->map.end();
|
||||
}
|
||||
|
||||
std::optional<std::reference_wrapper<const std::vector<ObjectTileHighlight>>>
|
||||
HighlightMap::GetForTile(TileIndex tile) const {
|
||||
auto it = this->map.find(tile);
|
||||
if (it == this->map.end()) return std::nullopt;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
HighlightMap::MapTypeKeys HighlightMap::GetAllTiles() const {
|
||||
return std::views::keys(this->map);
|
||||
}
|
||||
|
||||
std::vector<TileIndex> HighlightMap::UpdateWithMap(const HighlightMap &update) {
|
||||
std::vector<TileIndex> tiles_changed;
|
||||
for (auto it = this->map.begin(); it != this->map.end();) {
|
||||
tiles_changed.push_back(it->first);
|
||||
it = (update.Contains(it->first) ? std::next(it) : this->map.erase(it));
|
||||
}
|
||||
for (auto &[t, l] : update.GetMap()) {
|
||||
auto it = this->map.find(t);
|
||||
if (it != this->map.end() && it->second == l)
|
||||
continue;
|
||||
this->map.insert_or_assign(it, t, l);
|
||||
tiles_changed.push_back(t);
|
||||
}
|
||||
return tiles_changed;
|
||||
}
|
||||
|
||||
void HighlightMap::AddTileArea(const TileArea &area, SpriteID palette) {
|
||||
if (area.w == 0 || area.h == 0) return;
|
||||
|
||||
auto sx = TileX(area.tile), sy = TileY(area.tile);
|
||||
auto ex = sx + area.w - 1, ey = sy + area.h - 1;
|
||||
|
||||
for (auto y = sy; y <= ey; y++) {
|
||||
for (auto x = sx; x <= ex; x++) {
|
||||
this->Add(TileXY(x, y), ObjectTileHighlight::make_tint(palette));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HighlightMap::AddTileAreaWithBorder(const TileArea &area, SpriteID palette) {
|
||||
if (area.w == 0 || area.h == 0) return;
|
||||
|
||||
this->AddTileArea(area, palette);
|
||||
|
||||
auto sx = TileX(area.tile), sy = TileY(area.tile);
|
||||
auto ex = sx + area.w - 1, ey = sy + area.h - 1;
|
||||
|
||||
if (area.w == 1 && area.h == 1) {
|
||||
this->Add(area.tile, ObjectTileHighlight::make_border(palette, ZoningBorder::FULL));
|
||||
return;
|
||||
}
|
||||
// NOTE: Doesn't handle one-tile width/height separately but relies on border overlapping
|
||||
this->Add(TileXY(sx, sy), ObjectTileHighlight::make_border(palette, ZoningBorder::TOP_LEFT | ZoningBorder::TOP_RIGHT));
|
||||
for (auto x = sx + 1; x < ex; x++)
|
||||
this->Add(TileXY(x, sy), ObjectTileHighlight::make_border(palette, ZoningBorder::TOP_LEFT));
|
||||
this->Add(TileXY(ex, sy), ObjectTileHighlight::make_border(palette, ZoningBorder::TOP_LEFT | ZoningBorder::BOTTOM_LEFT));
|
||||
for (auto y = sy + 1; y < ey; y++) {
|
||||
this->Add(TileXY(sx, y), ObjectTileHighlight::make_border(palette, ZoningBorder::TOP_RIGHT));
|
||||
for (auto x = sx + 1; x < ex; x++) {
|
||||
this->Add(TileXY(x, y), ObjectTileHighlight::make_border(palette, ZoningBorder::NONE));
|
||||
}
|
||||
this->Add(TileXY(ex, y), ObjectTileHighlight::make_border(palette, ZoningBorder::BOTTOM_LEFT));
|
||||
}
|
||||
this->Add(TileXY(sx, ey), ObjectTileHighlight::make_border(palette, ZoningBorder::TOP_RIGHT | ZoningBorder::BOTTOM_RIGHT));
|
||||
for (auto x = sx + 1; x < ex; x++)
|
||||
this->Add(TileXY(x, ey), ObjectTileHighlight::make_border(palette, ZoningBorder::BOTTOM_RIGHT));
|
||||
this->Add(TileXY(ex, ey), ObjectTileHighlight::make_border(palette, ZoningBorder::BOTTOM_LEFT | ZoningBorder::BOTTOM_RIGHT));
|
||||
}
|
||||
|
||||
void HighlightMap::AddTilesBorder(const std::set<TileIndex> &tiles, SpriteID palette) {
|
||||
for (auto t : tiles) {
|
||||
auto b = CalcTileBorders(t, [&tiles](TileIndex t) {
|
||||
return tiles.find(t) == tiles.end() ? 0 : 1;
|
||||
});
|
||||
if (b.first != ZoningBorder::NONE)
|
||||
this->Add(t, ObjectTileHighlight::make_border(palette, b.first));
|
||||
}
|
||||
}
|
||||
|
||||
SpriteID GetTintBySelectionColour(SpriteID colour, bool deep=false) {
|
||||
switch(colour) {
|
||||
case CM_SPR_PALETTE_ZONING_RED: return (deep ? CM_PALETTE_TINT_RED_DEEP : CM_PALETTE_TINT_RED);
|
||||
@@ -1489,6 +1629,44 @@ TileHighlight ObjectHighlight::GetTileHighlight(const TileInfo *ti) {
|
||||
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->layouts.size()) 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) {
|
||||
switch (oth.type) {
|
||||
case ObjectTileHighlight::Type::RAIL_DEPOT:
|
||||
@@ -1617,34 +1795,6 @@ void ObjectHighlight::DrawOverlay([[maybe_unused]] DrawPixelInfo *dpi) {
|
||||
if (!this->cost.Succeeded()) return;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
uint8 Get(uint32 x, uint32 y, F getter) {
|
||||
if (x >= Map::SizeX() || y >= Map::SizeY()) return 0;
|
||||
return getter(TileXY(x, y));
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
std::pair<ZoningBorder, uint8> CalcTileBorders(TileIndex tile, F getter) {
|
||||
auto x = TileX(tile), y = TileY(tile);
|
||||
ZoningBorder res = ZoningBorder::NONE;
|
||||
auto z = getter(tile);
|
||||
if (z == 0)
|
||||
return std::make_pair(res, 0);
|
||||
auto tr = Get(x - 1, y, getter);
|
||||
auto tl = Get(x, y - 1, getter);
|
||||
auto bl = Get(x + 1, y, getter);
|
||||
auto br = Get(x, y + 1, getter);
|
||||
if (tr < z) res |= ZoningBorder::TOP_RIGHT;
|
||||
if (tl < z) res |= ZoningBorder::TOP_LEFT;
|
||||
if (bl < z) res |= ZoningBorder::BOTTOM_LEFT;
|
||||
if (br < z) res |= ZoningBorder::BOTTOM_RIGHT;
|
||||
if (tr == z && tl == z && Get(x - 1, y - 1, getter) < z) res |= ZoningBorder::TOP_CORNER;
|
||||
if (tr == z && br == z && Get(x - 1, y + 1, getter) < z) res |= ZoningBorder::RIGHT_CORNER;
|
||||
if (br == z && bl == z && Get(x + 1, y + 1, getter) < z) res |= ZoningBorder::BOTTOM_CORNER;
|
||||
if (tl == z && bl == z && Get(x + 1, y - 1, getter) < z) res |= ZoningBorder::LEFT_CORNER;
|
||||
return std::make_pair(res, z);
|
||||
}
|
||||
|
||||
static uint8 _industry_highlight_hash = 0;
|
||||
|
||||
void UpdateIndustryHighlight() {
|
||||
@@ -1814,9 +1964,10 @@ void CalcCBTownLimitBorder(TileHighlight &th, TileIndex tile, SpriteID border_pa
|
||||
|
||||
TileHighlight GetTileHighlight(const TileInfo *ti, TileType tile_type) {
|
||||
TileHighlight th;
|
||||
auto it = _ap.tiles.find(ti->tile);
|
||||
if (it != _ap.tiles.end()) {
|
||||
for (auto &oth : it->second) {
|
||||
|
||||
auto hl = _at.tiles.GetForTile(ti->tile);
|
||||
if (hl.has_value()) {
|
||||
for (auto &oth : hl.value().get()) {
|
||||
oth.SetTileHighlight(th, ti);
|
||||
}
|
||||
return th;
|
||||
@@ -1943,9 +2094,9 @@ void DrawTileZoning(const TileInfo *ti, const TileHighlight &th, TileType tile_t
|
||||
bool DrawTileSelection(const TileInfo *ti, [[maybe_unused]] const TileHighlightType &tht) {
|
||||
if (ti->tile == INVALID_TILE || IsTileType(ti->tile, MP_VOID)) return false;
|
||||
|
||||
auto it = _ap.tiles.find(ti->tile);
|
||||
if (it != _ap.tiles.end()) {
|
||||
for (auto oth : it->second) {
|
||||
auto hl = _at.tiles.GetForTile(ti->tile);
|
||||
if (hl.has_value()) {
|
||||
for (auto &oth : hl.value().get()) {
|
||||
DrawObjectTileHighlight(ti, oth);
|
||||
}
|
||||
return true;
|
||||
@@ -2270,7 +2421,8 @@ PaletteID GetTreeShadePal(TileIndex tile) {
|
||||
}
|
||||
}
|
||||
|
||||
ActivePreview _ap;
|
||||
ActiveTool _at;
|
||||
|
||||
|
||||
static void ResetVanillaHighlight() {
|
||||
if (_thd.window_class != WC_INVALID) {
|
||||
@@ -2296,37 +2448,76 @@ static void ResetVanillaHighlight() {
|
||||
_thd.make_square_red = false;
|
||||
}
|
||||
|
||||
void SetActivePreview(up<Preview> &&preview) {
|
||||
void SetActiveTool(up<Tool> &&tool) {
|
||||
ResetVanillaHighlight();
|
||||
ResetActivePreview();
|
||||
_ap.preview = std::move(preview);
|
||||
ResetActiveTool();
|
||||
_at.tool = std::move(tool);
|
||||
}
|
||||
|
||||
void ResetActivePreview() {
|
||||
for (auto &[t, l] : _ap.tiles) {
|
||||
void ResetActiveTool() {
|
||||
for (auto t : _at.tiles.GetAllTiles()) {
|
||||
MarkTileDirtyByTile(t);
|
||||
}
|
||||
_ap.preview = nullptr;
|
||||
_ap.tiles = {};
|
||||
_at.tool = nullptr;
|
||||
_at.tiles = {};
|
||||
}
|
||||
|
||||
void UpdateActivePreview() {
|
||||
if (_ap.preview == nullptr) return;
|
||||
const up<Tool> &GetActiveTool() {
|
||||
return _at.tool;
|
||||
}
|
||||
|
||||
|
||||
void UpdateActiveTool() {
|
||||
Point pt = GetTileBelowCursor();
|
||||
auto tile = pt.x == -1 ? INVALID_TILE : TileVirtXY(pt.x, pt.y);
|
||||
_ap.preview->Update(pt, tile);
|
||||
|
||||
auto tiles = _ap.preview->GetTiles();
|
||||
for (auto it = _ap.tiles.begin(); it != _ap.tiles.end();) {
|
||||
MarkTileDirtyByTile(it->first);
|
||||
it = (tiles.find(it->first) == tiles.end() ? _ap.tiles.erase(it) : std::next(it));
|
||||
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();
|
||||
}
|
||||
for (auto &[t, l] : tiles) {
|
||||
auto it = _ap.tiles.find(t);
|
||||
if (it != _ap.tiles.end() && it->second == l)
|
||||
continue;
|
||||
_ap.tiles.insert_or_assign(it, t, l);
|
||||
auto [hlmap, overlay_data, cost] = info;
|
||||
auto tiles_changed = _at.tiles.UpdateWithMap(hlmap);
|
||||
for (auto t : tiles_changed)
|
||||
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) {
|
||||
overlay_data.emplace_back(0, PAL_NONE, GetString(CM_STR_BUILD_INFO_OVERLAY_COST_OK, money));
|
||||
}
|
||||
} else if (err == STR_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY) {
|
||||
overlay_data.emplace_back(0, PAL_NONE, GetString(CM_STR_BUILD_INFO_OVERLAY_COST_NO_MONEY, cost.GetCost()));
|
||||
} else {
|
||||
if (err == INVALID_STRING_ID) {
|
||||
overlay_data.emplace_back(0, PAL_NONE, GetString(CM_STR_BUILD_INFO_OVERLAY_ERROR_UNKNOWN));
|
||||
} else {
|
||||
overlay_data.emplace_back(0, PAL_NONE, GetString(CM_STR_BUILD_INFO_OVERLAY_ERROR, err));
|
||||
}
|
||||
auto extra_err = cost.GetExtraErrorMessage();
|
||||
if (extra_err != INVALID_STRING_ID) {
|
||||
overlay_data.emplace_back(0, PAL_NONE, GetString(CM_STR_BUILD_INFO_OVERLAY_ERROR, extra_err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2342,35 +2533,35 @@ bool HandleMouseMove() {
|
||||
bool released = !_left_button_down && changed && _keep_mouse_click;
|
||||
if (!_left_button_down) _keep_mouse_click = false;
|
||||
|
||||
if (_ap.preview == nullptr) return false;
|
||||
if (_at.tool == nullptr) return false;
|
||||
// Viewport *vp = IsPtInWindowViewport(w, );
|
||||
|
||||
auto pt = GetTileBelowCursor();
|
||||
if (pt.x == -1) return false;
|
||||
auto tile = pt.x == -1 ? INVALID_TILE : TileVirtXY(pt.x, pt.y);
|
||||
_ap.preview->Update(pt, tile);
|
||||
_ap.preview->HandleMouseMove();
|
||||
_at.tool->Update(pt, tile);
|
||||
_at.tool->HandleMouseMove();
|
||||
if (_left_button_down) {
|
||||
if (changed && _ap.preview->HandleMousePress()) {
|
||||
if (changed && _at.tool->HandleMousePress()) {
|
||||
_keep_mouse_click = true;
|
||||
}
|
||||
if (_keep_mouse_click) return true;
|
||||
}
|
||||
if (released) {
|
||||
_ap.preview->HandleMouseRelease();
|
||||
_at.tool->HandleMouseRelease();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HandleMouseClick(Viewport *vp, bool double_click) {
|
||||
if (_ap.preview == nullptr) return false;
|
||||
if (_at.tool == nullptr) return false;
|
||||
auto pt = GetTileBelowCursor();
|
||||
auto tile = pt.x == -1 ? INVALID_TILE : TileVirtXY(pt.x, pt.y);
|
||||
_ap.preview->Update(pt, tile);
|
||||
return _ap.preview->HandleMouseClick(vp, pt, tile, double_click);
|
||||
_at.tool->Update(pt, tile);
|
||||
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 (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP);
|
||||
@@ -2383,13 +2574,13 @@ bool HandlePlacePushButton(Window *w, WidgetID widget, up<Preview> preview) {
|
||||
|
||||
w->LowerWidget(widget);
|
||||
|
||||
auto icon = preview->GetCursor();
|
||||
auto icon = tool->GetCursor();
|
||||
if ((icon & ANIMCURSOR_FLAG) != 0) {
|
||||
SetAnimatedMouseCursor(_animcursors[icon & ~ANIMCURSOR_FLAG]);
|
||||
} else {
|
||||
SetMouseCursor(icon, PAL_NONE);
|
||||
}
|
||||
citymania::SetActivePreview(std::move(preview));
|
||||
citymania::SetActiveTool(std::move(tool));
|
||||
_thd.window_class = w->window_class;
|
||||
_thd.window_number = w->window_number;
|
||||
|
||||
@@ -2397,5 +2588,4 @@ bool HandlePlacePushButton(Window *w, WidgetID widget, up<Preview> preview) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
} // namespace citymania
|
||||
|
||||
@@ -57,11 +57,13 @@ PaletteID GetTreeShadePal(TileIndex tile);
|
||||
void RotateAutodetection();
|
||||
void ResetRotateAutodetection();
|
||||
|
||||
void ResetActivePreview();
|
||||
void SetActivePreview(up<Preview> &&preview);
|
||||
void UpdateActivePreview();
|
||||
void ResetActiveTool();
|
||||
void SetActiveTool(up<Tool> &&tool);
|
||||
void UpdateActiveTool();
|
||||
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 HandleMouseClick(Viewport *vp, bool double_click);
|
||||
|
||||
|
||||
@@ -15,12 +15,15 @@
|
||||
#include "../track_type.h"
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <ranges>
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "cm_command_type.hpp"
|
||||
#include "cm_overlays.hpp"
|
||||
|
||||
|
||||
namespace citymania {
|
||||
@@ -311,6 +314,26 @@ public:
|
||||
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 {
|
||||
public:
|
||||
enum Type : uint8_t {
|
||||
@@ -323,6 +346,7 @@ public:
|
||||
BLUEPRINT = 6,
|
||||
POLYRAIL = 7,
|
||||
INDUSTRY = 8,
|
||||
DOCK = 9,
|
||||
};
|
||||
|
||||
Type type = Type::NONE;
|
||||
@@ -350,7 +374,7 @@ protected:
|
||||
bool tiles_updated = false;
|
||||
std::multimap<TileIndex, ObjectTileHighlight> tiles;
|
||||
std::vector<DetachedHighlight> sprites = {};
|
||||
std::vector<std::pair<SpriteID, std::string>> overlay_data = {};
|
||||
BuildInfoOverlayData overlay_data = {};
|
||||
// Point overlay_pos = {0, 0};
|
||||
void AddTile(TileIndex tile, ObjectTileHighlight &&oh);
|
||||
// void AddSprite(TileIndex tile, ObjectTileHighlight &&oh);
|
||||
@@ -371,8 +395,11 @@ public:
|
||||
TileIndex start_tile2, TileIndex end_tile2, Trackdir trackdir2);
|
||||
|
||||
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);
|
||||
HighlightMap GetHighlightMap(SpriteID palette);
|
||||
std::optional<TileArea> GetArea();
|
||||
void Draw(const TileInfo *ti);
|
||||
void DrawSelectionOverlay(DrawPixelInfo *dpi);
|
||||
void DrawOverlay(DrawPixelInfo *dpi);
|
||||
@@ -382,16 +409,48 @@ public:
|
||||
void MarkDirty();
|
||||
};
|
||||
|
||||
typedef std::tuple<HighlightMap, BuildInfoOverlayData, CommandCost> ToolGUIInfo;
|
||||
|
||||
class Preview {
|
||||
public:
|
||||
typedef std::map<TileIndex, std::vector<ObjectTileHighlight>> TileMap;
|
||||
virtual ~Preview() {}
|
||||
virtual void Update(Point pt, TileIndex tile) = 0;
|
||||
virtual void HandleMouseMove() {};
|
||||
virtual bool HandleMousePress() { return false; };
|
||||
virtual void HandleMouseRelease() {};
|
||||
virtual bool HandleMouseClick(Viewport* /* vp */, Point /* pt */, TileIndex /* tile */, bool /* double_click */) { return false; };
|
||||
virtual TileMap GetTiles() = 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 void OnStationRemoved(const Station* /* station */) {};
|
||||
};
|
||||
@@ -403,12 +462,13 @@ public:
|
||||
// DragStop,
|
||||
// };
|
||||
|
||||
struct ActivePreview {
|
||||
up<Preview> preview;
|
||||
Preview::TileMap tiles;
|
||||
struct ActiveTool {
|
||||
up<Tool> tool;
|
||||
HighlightMap tiles;
|
||||
};
|
||||
|
||||
extern ActivePreview _ap;
|
||||
extern ActiveTool _at;
|
||||
|
||||
|
||||
} // namespace citymania
|
||||
|
||||
|
||||
@@ -1773,30 +1773,6 @@ int SmallMapWindow::GetPositionOnLegend(Point pt)
|
||||
}
|
||||
}
|
||||
|
||||
/* virtual */ void SmallMapWindow::OnRealtimeTick(uint /* delta_ms */)
|
||||
{
|
||||
/* Update the window every now and then */
|
||||
if (this->map_type == SMT_LINKSTATS) {
|
||||
CompanyMask company_mask = this->GetOverlayCompanyMask();
|
||||
if (this->overlay->GetCompanyMask() != company_mask) {
|
||||
this->overlay->SetCompanyMask(company_mask);
|
||||
} else {
|
||||
this->overlay->SetDirty();
|
||||
}
|
||||
}
|
||||
_smallmap_industry_highlight_state = !_smallmap_industry_highlight_state;
|
||||
|
||||
this->SetDirty();
|
||||
}
|
||||
|
||||
// /* virtual */ void SmallMapWindow::OnHundredthTick()
|
||||
// {
|
||||
// if (this->show_towns) {
|
||||
// this->UpdateTownCache(true);
|
||||
// this->SetDirty();
|
||||
// }
|
||||
// }
|
||||
|
||||
/** Update all the links on the map. */
|
||||
void SmallMapWindow::UpdateLinks()
|
||||
{
|
||||
@@ -1826,6 +1802,7 @@ void SmallMapWindow::ForceRefresh()
|
||||
{
|
||||
if (_smallmap_industry_highlight != IT_INVALID) return;
|
||||
|
||||
if (this->show_towns) this->UpdateTownCache(true);
|
||||
this->UpdateLinks();
|
||||
this->SetDirty();
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ protected:
|
||||
}
|
||||
|
||||
/** Blink the industries (if selected) on a regular interval. */
|
||||
IntervalTimer<TimerWindow> blink_interval = {std::chrono::milliseconds(450), [this](auto) {
|
||||
IntervalTimer<TimerWindow> blink_interval = {TIMER_BLINK_INTERVAL, [this](auto) {
|
||||
Blink();
|
||||
}};
|
||||
|
||||
@@ -244,7 +244,6 @@ public:
|
||||
void OnInvalidateData(int data = 0, bool gui_scope = true) override;
|
||||
bool OnRightClick(Point pt, int widget) override;
|
||||
void OnMouseWheel(int wheel) override;
|
||||
void OnRealtimeTick(uint delta_ms) override;
|
||||
// void OnHundredthTick() override;
|
||||
void OnScroll(Point delta) override;
|
||||
void OnMouseOver(Point pt, int widget) override;
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
}
|
||||
|
||||
void UpdateSize() {
|
||||
this->padding = ScaleGUITrad(3);
|
||||
this->padding = ScaleGUITrad(5);
|
||||
auto dim = this->GetContentDimension();
|
||||
this->box.width = dim.width + 2 * this->padding;
|
||||
this->box.height = dim.height + 2 * this->padding;
|
||||
@@ -180,8 +180,9 @@ public:
|
||||
|
||||
Dimension text_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.width += WidgetDimensions::scaled.hsep_indent * indent;
|
||||
if (icon != PAL_NONE)
|
||||
icon_dim = maxdim(icon_dim, GetSpriteSize(icon));
|
||||
}
|
||||
@@ -191,25 +192,26 @@ public:
|
||||
if (icon_dim.width > 0)
|
||||
this->text_ofs_x = icon_dim.width + this->padding;
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
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 ofs_x = 0;
|
||||
int ind = WidgetDimensions::scaled.hsep_indent * indent;
|
||||
if (icon != PAL_NONE) {
|
||||
icon_height = GetSpriteSize(icon).height;
|
||||
ofs_x = this->text_ofs_x;
|
||||
}
|
||||
return {
|
||||
{
|
||||
rect.left,
|
||||
rect.left + ind,
|
||||
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.right,
|
||||
rect.bottom
|
||||
@@ -226,6 +228,11 @@ public:
|
||||
~BuildInfoOverlay() override {}
|
||||
|
||||
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->y = y;
|
||||
this->data = data;
|
||||
@@ -247,9 +254,8 @@ public:
|
||||
|
||||
void DrawContent(Rect rect) override {
|
||||
int row = 0;
|
||||
for (const auto &[icon, s] : this->data) {
|
||||
auto [ipos, srect] = this->aligner.GetPositions(rect, row, icon);
|
||||
|
||||
for (const auto &[indent, icon, s] : this->data) {
|
||||
auto [ipos, srect] = this->aligner.GetPositions(rect, row, indent, icon);
|
||||
DrawString(srect.left, srect.right, srect.top, s);
|
||||
if (icon != PAL_NONE)
|
||||
DrawSprite(icon, PAL_NONE, ipos.x, ipos.y);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
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 DrawOverlays();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
#ifndef CM_STATION_GUI_HPP
|
||||
#define CM_STATION_GUI_HPP
|
||||
|
||||
#include "cm_command_type.hpp"
|
||||
#include "cm_highlight_type.hpp"
|
||||
|
||||
#include "../core/geometry_type.hpp"
|
||||
@@ -9,6 +10,9 @@
|
||||
#include "../station_gui.h"
|
||||
#include "../station_type.h"
|
||||
|
||||
#include <concepts>
|
||||
#include <optional>
|
||||
|
||||
namespace citymania {
|
||||
|
||||
const DiagDirection DEPOTDIR_AUTO = DIAGDIR_END;
|
||||
@@ -26,38 +30,25 @@ struct RailStationGUISettings {
|
||||
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);
|
||||
bool UseImprovedStationJoin();
|
||||
void OnStationTileSetChange(const Station *station, bool adding, StationType type);
|
||||
void OnStationPartBuilt(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();
|
||||
void MarkCoverageHighlightDirty();
|
||||
bool CheckRedrawStationCoverage();
|
||||
void AbortStationPlacement();
|
||||
|
||||
std::string GetStationCoverageProductionText(TileIndex tile, int w, int h, int rad, StationCoverageType sct);
|
||||
std::optional<std::string> GetStationCoverageAreaText(TileIndex tile, int w, int h, int rad, StationCoverageType sct, bool supplies);
|
||||
|
||||
bool CheckDriveThroughRoadStopDirection(TileArea area, RoadBits r);
|
||||
DiagDirection AutodetectRoadObjectDirection(TileIndex tile, Point pt, RoadType roadtype);
|
||||
DiagDirection AutodetectDriveThroughRoadStopDirection(TileArea area, Point pt, RoadType roadtype);
|
||||
DiagDirection AutodetectRailObjectDirection(TileIndex tile, Point pt);
|
||||
void SetSelectedStationToJoin(StationID station_id);
|
||||
void ResetJoinStationHighlight();
|
||||
|
||||
|
||||
struct OverlayParams {
|
||||
@@ -66,142 +57,305 @@ struct OverlayParams {
|
||||
StationCoverageType coverage_type;
|
||||
};
|
||||
|
||||
|
||||
class PreviewStationType {
|
||||
// Remove action classes
|
||||
class RemoveHandler {
|
||||
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 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(Preview::TileMap &tiles, 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:
|
||||
virtual ~RailStationPreview() {};
|
||||
|
||||
// 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(Preview::TileMap &tiles, 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;
|
||||
RemoveAction(const Handler &handler) : handler{handler} {}
|
||||
~RemoveAction() override = default;
|
||||
void Update(Point pt, TileIndex tile) override;
|
||||
up<Command> GetCommand(bool adjacent, StationID join_to) const override;
|
||||
up<Command> GetRemoveCommand() const override;
|
||||
void AddPreviewTiles(Preview::TileMap &tiles, SpriteID palette) const override;
|
||||
bool Execute(up<Command> cmd, bool remove_mode) const override;
|
||||
OverlayParams GetOverlayParams() const override;
|
||||
std::optional<TileArea> GetArea() const override;
|
||||
bool HandleMousePress() override;
|
||||
void HandleMouseRelease() override;
|
||||
ToolGUIInfo GetGUIInfo() override;
|
||||
void OnStationRemoved(const Station *) override;
|
||||
};
|
||||
|
||||
class DockPreview : public PreviewStationType {
|
||||
protected:
|
||||
DiagDirection ddir;
|
||||
|
||||
// StationSelect classes
|
||||
class StationSelectHandler {
|
||||
public:
|
||||
DockPreview() {}
|
||||
virtual ~DockPreview() {};
|
||||
|
||||
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(Preview::TileMap &tiles, SpriteID palette) const override;
|
||||
bool Execute(up<Command> cmd, bool remove_mode) const override;
|
||||
OverlayParams GetOverlayParams() const override;
|
||||
virtual ~StationSelectHandler() = default;
|
||||
virtual void SelectStationToJoin(StationID station_id) = 0;
|
||||
};
|
||||
template<typename Handler>
|
||||
concept ImplementsStationSelectHandler = std::derived_from<Handler, StationSelectHandler>;
|
||||
|
||||
class StationPreviewBase : public Preview {
|
||||
protected:
|
||||
sp<PreviewStationType> type;
|
||||
bool remove_mode = false;
|
||||
bool keep_rail = true; // whether to keep rail in remove mode
|
||||
StationID station_to_join = StationID::Invalid();
|
||||
bool show_coverage = true;
|
||||
|
||||
void AddAreaTiles(Preview::TileMap &tiles, bool add_current, bool show_join_area);
|
||||
virtual void Execute() = 0;
|
||||
up<Command> GetCommand(bool adjacent, StationID join_to);
|
||||
void AddStationPreview(Preview::TileMap &tiles, SpriteID palette);
|
||||
|
||||
template<ImplementsStationSelectHandler Handler>
|
||||
class StationSelectAction : public Action {
|
||||
private:
|
||||
Handler handler;
|
||||
TileIndex cur_tile = INVALID_TILE;
|
||||
StationID selected_station = StationID::Invalid();
|
||||
public:
|
||||
StationPreviewBase(sp<PreviewStationType> type) :type{type} {};
|
||||
CursorID GetCursor() override { return this->type->GetCursor(); };
|
||||
StationSelectAction(const Handler &handler) : handler{handler} {}
|
||||
~StationSelectAction() override = default;
|
||||
void Update(Point pt, TileIndex tile) override;
|
||||
bool HandleMousePress() override;
|
||||
void HandleMouseRelease() 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 = StationID::Invalid();
|
||||
|
||||
VanillaStationPreview(sp<PreviewStationType> type) :StationPreviewBase{type} {};
|
||||
virtual ~VanillaStationPreview() {};
|
||||
void Update(Point pt, TileIndex tile) override;
|
||||
|
||||
Preview::TileMap GetTiles() override;
|
||||
ToolGUIInfo GetGUIInfo() override;
|
||||
void OnStationRemoved(const Station *station) override;
|
||||
};
|
||||
|
||||
|
||||
class StationPreview : public StationPreviewBase {
|
||||
protected:
|
||||
bool select_mode = false;
|
||||
|
||||
void Execute() override;
|
||||
up<Command> GetCommand();
|
||||
|
||||
// PlacementAction
|
||||
class PlacementAction : public Action {
|
||||
public:
|
||||
StationPreview(sp<PreviewStationType> type);
|
||||
virtual ~StationPreview();
|
||||
~PlacementAction() override = default;
|
||||
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;
|
||||
std::optional<TileArea> GetArea() const override { return std::nullopt; };
|
||||
bool HandleMousePress() override;
|
||||
|
||||
Preview::TileMap GetTiles() override;
|
||||
void OnStationRemoved(const Station *station) override;
|
||||
void HandleMouseRelease() override;
|
||||
ToolGUIInfo GetGUIInfo() 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 == RoadStopType::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
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
@@ -72,6 +73,56 @@ enum class ControllerType: uint8_t {
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -271,16 +271,14 @@ public:
|
||||
Commands get_command() override;
|
||||
};
|
||||
|
||||
class BuildAirport: public Command {
|
||||
class BuildAirport: public StationBuildCommand {
|
||||
public:
|
||||
TileIndex tile;
|
||||
uint8_t airport_type;
|
||||
uint8_t layout;
|
||||
StationID station_to_join;
|
||||
bool adjacent;
|
||||
|
||||
BuildAirport(TileIndex tile, uint8_t airport_type, uint8_t 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 {}
|
||||
|
||||
bool _post(::CommandCallback * callback) override;
|
||||
@@ -288,14 +286,12 @@ public:
|
||||
Commands get_command() override;
|
||||
};
|
||||
|
||||
class BuildDock: public Command {
|
||||
class BuildDock: public StationBuildCommand {
|
||||
public:
|
||||
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 {}
|
||||
|
||||
bool _post(::CommandCallback * callback) override;
|
||||
@@ -303,7 +299,7 @@ public:
|
||||
Commands get_command() override;
|
||||
};
|
||||
|
||||
class BuildRailStation: public Command {
|
||||
class BuildRailStation: public StationBuildCommand {
|
||||
public:
|
||||
TileIndex tile_org;
|
||||
RailType rt;
|
||||
@@ -312,11 +308,9 @@ public:
|
||||
uint8_t plat_len;
|
||||
StationClassID spec_class;
|
||||
uint16_t spec_index;
|
||||
StationID station_to_join;
|
||||
bool adjacent;
|
||||
|
||||
BuildRailStation(TileIndex tile_org, RailType rt, Axis axis, uint8_t numtracks, uint8_t 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 {}
|
||||
|
||||
bool _post(::CommandCallback * callback) override;
|
||||
@@ -339,7 +333,7 @@ public:
|
||||
Commands get_command() override;
|
||||
};
|
||||
|
||||
class BuildRoadStop: public Command {
|
||||
class BuildRoadStop: public StationBuildCommand {
|
||||
public:
|
||||
TileIndex tile;
|
||||
uint8_t width;
|
||||
@@ -350,11 +344,9 @@ public:
|
||||
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 {}
|
||||
|
||||
bool _post(::CommandCallback * callback) override;
|
||||
|
||||
@@ -38,10 +38,12 @@
|
||||
#include "table/sprites.h"
|
||||
#include "table/strings.h"
|
||||
|
||||
#include "citymania/cm_highlight.hpp"
|
||||
#include "citymania/cm_hotkeys.hpp"
|
||||
#include "citymania/cm_station_gui.hpp"
|
||||
|
||||
#include "safeguards.h"
|
||||
#include <memory>
|
||||
|
||||
static void ShowBuildDockStationPicker(Window *parent);
|
||||
static void ShowBuildDocksDepotPicker(Window *parent);
|
||||
@@ -174,8 +176,8 @@ struct BuildDocksToolbarWindow : Window {
|
||||
break;
|
||||
|
||||
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 (citymania::HandleStationPlacePushButton(this, WID_DT_STATION, std::make_shared<citymania::DockPreview>())) {
|
||||
// if (HandlePlacePushButton(this, WID_DT_STATION, SPR_CURSOR_DOCK, HT_SPECIAL, CM_DDSP_BUILD_DOCK)) {
|
||||
if (citymania::HandlePlacePushButton(this, WID_DT_STATION, std::make_unique<citymania::DockBuildTool>())) {
|
||||
ShowBuildDockStationPicker(this);
|
||||
}
|
||||
|
||||
@@ -221,14 +223,13 @@ struct BuildDocksToolbarWindow : Window {
|
||||
break;
|
||||
|
||||
case WID_DT_STATION: { // Build station button
|
||||
NOT_REACHED(); // CityMania uses tools
|
||||
|
||||
/* Determine the watery part of the dock. */
|
||||
/*
|
||||
DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(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;
|
||||
auto proc = [=](bool test, StationID to_join) -> bool {
|
||||
@@ -240,7 +241,7 @@ struct BuildDocksToolbarWindow : Window {
|
||||
};
|
||||
|
||||
ShowSelectStationIfNeeded(TileArea(tile, tile_to), proc);
|
||||
break;
|
||||
break; */
|
||||
}
|
||||
|
||||
case WID_DT_BUOY: // Build buoy button
|
||||
|
||||
@@ -6362,13 +6362,26 @@ CM_BUILDING_PREVIEW_COST_ENOUGH :Cost
|
||||
|
||||
CM_STR_NO_BLUEPRINT_IN_SLOT :{WHITE}No blueprint in slot {NUM}
|
||||
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_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_CARGO :{WHITE}{CARGO_LONG}
|
||||
CM_STR_BUILD_INFO_OVERLAY_ERROR :{RED}{STRING}
|
||||
CM_STR_BUILD_INFO_OVERLAY_STATION_CARGO :{GOLD}{CARGO_LONG}
|
||||
CM_STR_BUILD_INFO_OVERLAY_ERROR :{RED}Error: {STRING}
|
||||
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}
|
||||
|
||||
@@ -6380,3 +6393,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_SHIP :{BLACK}Show hidden ({NUM})
|
||||
CM_STR_SHOW_HIDDEN_ENGINES_VEHICLE_AIRCRAFT :{BLACK}Show hidden ({NUM})
|
||||
|
||||
|
||||
@@ -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);
|
||||
// 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);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -208,6 +208,7 @@ void CcStation(Commands, const CommandCost &result, TileIndex tile)
|
||||
*/
|
||||
static void PlaceRail_Station(TileIndex tile)
|
||||
{
|
||||
NOT_REACHED(); // CityMania uses tools
|
||||
if (_remove_button_clicked) {
|
||||
VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_STATION);
|
||||
VpSetPlaceSizingLimit(-1);
|
||||
@@ -215,10 +216,6 @@ static void PlaceRail_Station(TileIndex tile)
|
||||
VpStartPlaceSizing(tile, VPM_X_AND_Y_LIMITED, DDSP_BUILD_STATION);
|
||||
VpSetPlaceSizingLimit(_settings_game.station.station_spread);
|
||||
} else {
|
||||
if (citymania::UseImprovedStationJoin()) {
|
||||
citymania::PlaceRail_Station(tile);
|
||||
return;
|
||||
}
|
||||
int w = _settings_client.gui.station_numtracks;
|
||||
int h = _settings_client.gui.station_platlength;
|
||||
if (!_station_gui.axis) std::swap(w, h);
|
||||
@@ -684,12 +681,14 @@ struct BuildRailToolbarWindow : Window {
|
||||
if (was_open) ResetObjectToPlace();
|
||||
if (!was_open || dragdrop != _settings_client.gui.station_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))
|
||||
ShowStationBuilder(this);
|
||||
}
|
||||
this->last_user_action = WID_RAT_BUILD_STATION;
|
||||
} 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)) {
|
||||
ShowStationBuilder(this);
|
||||
this->last_user_action = WID_RAT_BUILD_STATION;
|
||||
}
|
||||
@@ -1126,11 +1125,7 @@ Window *ShowBuildRailToolbar(RailType railtype)
|
||||
|
||||
static void HandleStationPlacement(TileIndex start, TileIndex end)
|
||||
{
|
||||
if (citymania::UseImprovedStationJoin()) {
|
||||
citymania::HandleStationPlacement(start, end);
|
||||
return;
|
||||
}
|
||||
|
||||
NOT_REACHED(); // CityMania uses tools
|
||||
TileArea ta(start, end);
|
||||
uint numtracks = ta.w;
|
||||
uint platlength = ta.h;
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
#include "citymania/cm_station_gui.hpp"
|
||||
|
||||
#include "safeguards.h"
|
||||
#include <memory>
|
||||
|
||||
static void ShowRVStationPicker(Window *parent, RoadStopType rs);
|
||||
static void ShowRoadDepotPicker(Window *parent);
|
||||
@@ -235,11 +236,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)
|
||||
{
|
||||
if (citymania::UseImprovedStationJoin()) {
|
||||
citymania::PlaceRoadStop(start_tile, end_tile, stop_type, adjacent, rt, err_msg);
|
||||
return;
|
||||
}
|
||||
|
||||
NOT_REACHED(); // CityMania uses tools
|
||||
TileArea ta(start_tile, end_tile);
|
||||
assert(_thd.cm.type == citymania::ObjectHighlight::Type::ROAD_STOP);
|
||||
// DiagDirection ddir = _roadstop_gui.orientation;
|
||||
@@ -563,14 +560,16 @@ struct BuildRoadToolbarWindow : Window {
|
||||
break;
|
||||
|
||||
case WID_ROT_BUS_STATION:
|
||||
if (citymania::HandleStationPlacePushButton(this, WID_ROT_BUS_STATION, std::make_shared<citymania::RoadStationPreview>(RoadStopType::Bus))) {
|
||||
if (citymania::HandlePlacePushButton(this, WID_ROT_BUS_STATION, std::make_unique<citymania::RoadStopBuildTool>(RoadStopType::Bus))) {
|
||||
// if (HandlePlacePushButton(this, WID_ROT_BUS_STATION, SPR_CURSOR_BUS_STATION, HT_RECT, DDSP_BUILD_BUSSTOP)) {
|
||||
ShowRVStationPicker(this, RoadStopType::Bus);
|
||||
this->last_started_action = widget;
|
||||
}
|
||||
break;
|
||||
|
||||
case WID_ROT_TRUCK_STATION:
|
||||
if (citymania::HandleStationPlacePushButton(this, WID_ROT_TRUCK_STATION, std::make_shared<citymania::RoadStationPreview>(RoadStopType::Truck))) {
|
||||
if (citymania::HandlePlacePushButton(this, WID_ROT_TRUCK_STATION, std::make_unique<citymania::RoadStopBuildTool>(RoadStopType::Truck))) {
|
||||
// if (HandlePlacePushButton(this, WID_ROT_TRUCK_STATION, SPR_CURSOR_TRUCK_STATION, HT_RECT, DDSP_BUILD_TRUCKSTOP)) {
|
||||
ShowRVStationPicker(this, RoadStopType::Truck);
|
||||
this->last_started_action = widget;
|
||||
}
|
||||
|
||||
@@ -773,7 +773,7 @@ protected:
|
||||
}
|
||||
|
||||
/** Blink the industries (if selected) on a regular interval. */
|
||||
IntervalTimer<TimerWindow> blink_interval = {std::chrono::milliseconds(450), [this](auto) {
|
||||
IntervalTimer<TimerWindow> blink_interval = {TIMER_BLINK_INTERVAL, [this](auto) {
|
||||
Blink();
|
||||
}};
|
||||
|
||||
|
||||
@@ -84,13 +84,6 @@ int DrawStationCoverageAreaText(const Rect &r, StationCoverageType sct, int rad,
|
||||
TileIndex tile = TileVirtXY(_thd.pos.x, _thd.pos.y);
|
||||
CargoTypes cargo_mask = 0;
|
||||
if (_thd.drawstyle == HT_RECT && tile < Map::Size()) {
|
||||
/* CityMania code begin */
|
||||
if (supplies) {
|
||||
auto s = citymania::GetStationCoverageProductionText(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad, sct);
|
||||
return DrawStringMultiLine(r, s);
|
||||
}
|
||||
/* CityMania code end */
|
||||
|
||||
CargoArray cargoes;
|
||||
if (supplies) {
|
||||
cargoes = GetProductionAroundTiles(tile, _thd.size.x / TILE_SIZE, _thd.size.y / TILE_SIZE, rad);
|
||||
@@ -189,14 +182,9 @@ void FindStationsAroundSelection()
|
||||
*/
|
||||
void CheckRedrawStationCoverage(Window *w)
|
||||
{
|
||||
/* CityMania code begin */
|
||||
if (citymania::UseImprovedStationJoin()) {
|
||||
if (citymania::CheckRedrawStationCoverage()) w->SetDirty();
|
||||
return;
|
||||
}
|
||||
/* CityMania code end */
|
||||
|
||||
/* Test if ctrl state changed */
|
||||
/* CityMania uses tools and handles redraws differently
|
||||
|
||||
static bool _last_fn_pressed;
|
||||
if (citymania::_fn_mod != _last_fn_pressed) {
|
||||
_thd.dirty = 0xff;
|
||||
@@ -211,6 +199,7 @@ void CheckRedrawStationCoverage(Window *w)
|
||||
FindStationsAroundSelection<StationTypeFilter>();
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -2376,7 +2365,7 @@ struct SelectStationWindow : Window {
|
||||
void Close([[maybe_unused]] int data = 0) override
|
||||
{
|
||||
if constexpr (std::is_same_v<T, Waypoint *>) SetViewportCatchmentSpecializedStation<typename T::StationType>(nullptr, true);
|
||||
else citymania::SetSelectedStationToJoin(StationID::Invalid());
|
||||
else citymania::ResetJoinStationHighlight();
|
||||
|
||||
_thd.freeze = false;
|
||||
this->Window::Close();
|
||||
@@ -2474,7 +2463,7 @@ struct SelectStationWindow : Window {
|
||||
auto it = this->vscroll->GetScrolledItemFromWidget(_stations_nearby_list, pt.y, this, WID_JS_PANEL, WidgetDimensions::scaled.framerect.top);
|
||||
const typename T::StationType *st = it == _stations_nearby_list.end() || *it == NEW_STATION ? nullptr : T::StationType::Get(*it);
|
||||
if constexpr (std::is_same_v<typename T::StationType, Waypoint>) SetViewportCatchmentSpecializedStation<typename T::StationType>(st, true);
|
||||
else citymania::SetSelectedStationToJoin(*it);
|
||||
else citymania::SetSelectedStationToJoin(st ? st->index : StationID::Invalid());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#define TILEAREA_TYPE_H
|
||||
|
||||
#include "map_func.h"
|
||||
#include "tile_type.h"
|
||||
|
||||
class OrthogonalTileIterator;
|
||||
class DiagonalTileIterator;
|
||||
@@ -62,6 +63,12 @@ struct OrthogonalTileArea {
|
||||
return TileAddXY(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 TileAddXY(this->tile, this->w - 1, this->h - 1);
|
||||
}
|
||||
|
||||
OrthogonalTileIterator begin() const;
|
||||
|
||||
OrthogonalTileIterator end() const;
|
||||
|
||||
@@ -32,4 +32,7 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
/** Interval used by blinking interface elements. */
|
||||
static constexpr std::chrono::milliseconds TIMER_BLINK_INTERVAL{450};
|
||||
|
||||
#endif /* TIMER_WINDOW_H */
|
||||
|
||||
@@ -2975,7 +2975,7 @@ void UpdateTileSelection()
|
||||
if ((new_drawstyle & HT_DRAG_MASK) != HT_NONE) SetSelectionTilesDirty();
|
||||
}
|
||||
|
||||
citymania::UpdateActivePreview();
|
||||
citymania::UpdateActiveTool();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4056,7 +4056,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)
|
||||
{
|
||||
citymania::ResetActivePreview();
|
||||
citymania::ResetActiveTool();
|
||||
if (_thd.window_class != WC_INVALID) {
|
||||
/* Undo clicking on button and drag & drop */
|
||||
Window *w = _thd.GetCallbackWnd();
|
||||
@@ -4107,7 +4107,7 @@ void SetObjectToPlace(CursorID icon, PaletteID pal, HighLightStyle mode, WindowC
|
||||
void ResetObjectToPlace()
|
||||
{
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user