diff --git a/src/citymania/cm_highlight.cpp b/src/citymania/cm_highlight.cpp index 3eb87646bd..e5b697316a 100644 --- a/src/citymania/cm_highlight.cpp +++ b/src/citymania/cm_highlight.cpp @@ -4,9 +4,11 @@ #include "cm_blueprint.hpp" #include "cm_commands.hpp" +#include "cm_highlight_type.hpp" #include "cm_main.hpp" #include "cm_overlays.hpp" #include "cm_station_gui.hpp" +#include "cm_type.hpp" #include "cm_zoning.hpp" #include "../core/math_func.hpp" @@ -20,6 +22,7 @@ #include "../newgrf_roadtype.h" #include "../newgrf_station.h" #include "../newgrf_industrytiles.h" +#include "../sound_func.h" #include "../spritecache.h" #include "../strings_func.h" #include "../town.h" @@ -29,22 +32,27 @@ #include "../tilehighlight_func.h" #include "../viewport_func.h" #include "../window_gui.h" +#include "../window_func.h" #include "../zoom_func.h" // #include "../zoning.h" #include "../table/airporttile_ids.h" +#include "../table/animcursors.h" #include "../table/track_land.h" #include "../table/autorail.h" #include "../table/industry_land.h" #include "../debug.h" +#include "generated/cm_gen_commands.hpp" #include "station_gui.h" #include "station_type.h" #include "table/sprites.h" #include "table/strings.h" #include "tile_type.h" +#include +#include +#include #include - /** Enumeration of multi-part foundations */ enum FoundationPart { FOUNDATION_PART_NONE = 0xFF, ///< Neither foundation nor groundsprite drawn yet. @@ -68,6 +76,7 @@ extern uint32 _cm_funding_layout; extern IndustryType _cm_funding_type; extern void GetStationLayout(byte *layout, uint numtracks, uint plat_len, const StationSpec *statspec); extern void IndustryDrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *group, byte rnd_colour, byte stage); +extern void SetSelectionTilesDirty(); struct RailStationGUISettings { Axis orientation; ///< Currently selected rail station orientation @@ -89,6 +98,72 @@ struct RoadStopGUISettings { extern RoadStopGUISettings _roadstop_gui_settings; +template <> +struct std::hash { + std::size_t operator()(const citymania::ObjectTileHighlight &oh) const { + std::size_t h = std::hash()(oh.palette); + h ^= hash()(oh.type); + switch (oh.type) { + case citymania::ObjectTileHighlight::Type::RAIL_DEPOT: + h ^= std::hash()(oh.u.rail.depot.ddir); + break; + case citymania::ObjectTileHighlight::Type::RAIL_TRACK: + h ^= std::hash()(oh.u.rail.track); + break; + case citymania::ObjectTileHighlight::Type::RAIL_STATION: + h ^= hash()(oh.u.rail.station.axis); + h ^= oh.u.rail.station.section; + break; + case citymania::ObjectTileHighlight::Type::RAIL_SIGNAL: + h ^= hash()(oh.u.rail.signal.pos); + h ^= hash()(oh.u.rail.signal.type); + h ^= hash()(oh.u.rail.signal.variant); + break; + case citymania::ObjectTileHighlight::Type::RAIL_BRIDGE_HEAD: + h ^= hash()(oh.u.rail.bridge_head.ddir); + h ^= hash()(oh.u.rail.bridge_head.other_end); + h ^= hash()(oh.u.rail.bridge_head.type); + break; + case citymania::ObjectTileHighlight::Type::RAIL_TUNNEL_HEAD: + h ^= hash()(oh.u.rail.tunnel_head.ddir); + break; + case citymania::ObjectTileHighlight::Type::ROAD_STOP: + h ^= hash()(oh.u.road.stop.ddir); + h ^= hash()(oh.u.road.stop.roadtype); + h ^= hash()(oh.u.road.stop.is_truck); + h ^= hash()(oh.u.road.stop.spec_class); + h ^= hash()(oh.u.road.stop.spec_index); + break; + case citymania::ObjectTileHighlight::Type::ROAD_DEPOT: + h ^= hash()(oh.u.road.depot.ddir); + h ^= hash()(oh.u.road.depot.roadtype); + break; + case citymania::ObjectTileHighlight::Type::AIRPORT_TILE: + h ^= hash()(oh.u.airport_tile.gfx); + break; + case citymania::ObjectTileHighlight::Type::INDUSTRY_TILE: + h ^= hash()(oh.u.industry_tile.gfx); + h ^= hash()(oh.u.industry_tile.ind_type); + h ^= oh.u.industry_tile.ind_layout; + h ^= hash()(oh.u.industry_tile.tile_diff); + break; + case citymania::ObjectTileHighlight::Type::NUMBERED_RECT: + h ^= hash()(oh.u.numbered_rect.number); + break; + case citymania::ObjectTileHighlight::Type::BORDER: + h ^= hash()(oh.u.border); + break; + case citymania::ObjectTileHighlight::Type::POINT: + case citymania::ObjectTileHighlight::Type::RECT: + case citymania::ObjectTileHighlight::Type::END: + case citymania::ObjectTileHighlight::Type::TINT: + case citymania::ObjectTileHighlight::Type::STRUCT_TINT: + break; + } + return h; + } +}; + namespace citymania { extern CargoArray GetProductionAroundTiles(TileIndex tile, int w, int h, int rad); @@ -111,7 +186,7 @@ extern StationBuildingStatus _station_building_status; extern const Station *_station_to_join; extern const Station *_highlight_station_to_join; extern TileArea _highlight_join_area; - +extern bool _fn_mod; std::set, std::greater>> _town_cache; // struct { @@ -222,6 +297,101 @@ ObjectTileHighlight ObjectTileHighlight::make_numbered_rect(SpriteID palette, ui return oh; } +ObjectTileHighlight ObjectTileHighlight::make_border(SpriteID palette, ZoningBorder border) { + auto oh = ObjectTileHighlight(Type::BORDER, palette); + oh.u.border = border; + return oh; +} + +ObjectTileHighlight ObjectTileHighlight::make_tint(SpriteID palette) { + auto oh = ObjectTileHighlight(Type::TINT, palette); + return oh; +} + +ObjectTileHighlight ObjectTileHighlight::make_struct_tint(SpriteID palette) { + auto oh = ObjectTileHighlight(Type::STRUCT_TINT, palette); + return oh; +} + +bool ObjectTileHighlight::operator==(const ObjectTileHighlight &oh) const { + if (this->type != oh.type) return false; + if (this->palette != oh.palette) return false; + switch (this->type) { + case ObjectTileHighlight::Type::RAIL_DEPOT: + return this->u.rail.depot.ddir == oh.u.rail.depot.ddir; + case ObjectTileHighlight::Type::RAIL_TRACK: + return this->u.rail.track == oh.u.rail.track; + case ObjectTileHighlight::Type::RAIL_STATION: + return this->u.rail.station.axis == oh.u.rail.station.axis + && this->u.rail.station.section == oh.u.rail.station.section; + case ObjectTileHighlight::Type::RAIL_SIGNAL: + return this->u.rail.signal.pos == oh.u.rail.signal.pos + && this->u.rail.signal.type == oh.u.rail.signal.type + && this->u.rail.signal.variant == oh.u.rail.signal.variant; + case ObjectTileHighlight::Type::RAIL_BRIDGE_HEAD: + return this->u.rail.bridge_head.ddir == oh.u.rail.bridge_head.ddir + && this->u.rail.bridge_head.other_end == oh.u.rail.bridge_head.other_end + && this->u.rail.bridge_head.type == oh.u.rail.bridge_head.type; + case ObjectTileHighlight::Type::RAIL_TUNNEL_HEAD: + return this->u.rail.tunnel_head.ddir == oh.u.rail.tunnel_head.ddir; + case ObjectTileHighlight::Type::ROAD_STOP: + return this->u.road.stop.ddir == oh.u.road.stop.ddir + && this->u.road.stop.roadtype == oh.u.road.stop.roadtype + && this->u.road.stop.is_truck == oh.u.road.stop.is_truck + && this->u.road.stop.spec_class == oh.u.road.stop.spec_class + && this->u.road.stop.spec_index == oh.u.road.stop.spec_index; + case ObjectTileHighlight::Type::ROAD_DEPOT: + return this->u.road.depot.ddir == oh.u.road.depot.ddir + && this->u.road.depot.roadtype == oh.u.road.depot.roadtype; + case ObjectTileHighlight::Type::AIRPORT_TILE: + return this->u.airport_tile.gfx == oh.u.airport_tile.gfx; + case ObjectTileHighlight::Type::INDUSTRY_TILE: + return this->u.industry_tile.gfx == oh.u.industry_tile.gfx + && this->u.industry_tile.ind_type == oh.u.industry_tile.ind_type + && this->u.industry_tile.ind_layout == oh.u.industry_tile.ind_layout + && this->u.industry_tile.tile_diff == oh.u.industry_tile.tile_diff; + case ObjectTileHighlight::Type::NUMBERED_RECT: + return this->u.numbered_rect.number == oh.u.numbered_rect.number; + case Type::BORDER: + return this->u.border == oh.u.border; + case Type::POINT: + case Type::RECT: + case Type::END: + case Type::TINT: + case Type::STRUCT_TINT: + return true; + } + return true; +} + +bool ObjectTileHighlight::SetTileHighlight(TileHighlight &th, const TileInfo *) const { + switch (this->type) { + case ObjectTileHighlight::Type::RAIL_DEPOT: + // case ObjectTileHighlight::Type::RAIL_TRACK: Depot track shouldn't remove foundation + case ObjectTileHighlight::Type::RAIL_STATION: + case ObjectTileHighlight::Type::RAIL_SIGNAL: + case ObjectTileHighlight::Type::RAIL_BRIDGE_HEAD: + case ObjectTileHighlight::Type::RAIL_TUNNEL_HEAD: + case ObjectTileHighlight::Type::ROAD_STOP: + case ObjectTileHighlight::Type::ROAD_DEPOT: + case ObjectTileHighlight::Type::AIRPORT_TILE: + case ObjectTileHighlight::Type::INDUSTRY_TILE: + 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; + case ObjectTileHighlight::Type::STRUCT_TINT: + th.structure_pal = this->palette; + return true; + + default: + break; + } + return false; +} bool ObjectHighlight::operator==(const ObjectHighlight& oh) const { if (this->type != oh.type) return false; @@ -240,6 +410,7 @@ bool ObjectHighlight::operator==(const ObjectHighlight& oh) const { && this->blueprint == oh.blueprint); } + bool ObjectHighlight::operator!=(const ObjectHighlight& oh) const { return !(*this == oh); } @@ -1268,95 +1439,100 @@ void DrawSelectionPoint(SpriteID palette, const TileInfo *ti) { DrawSelectionSprite(SPR_DOT, palette, ti, z, foundation_part); } +void DrawBorderSprites(const TileInfo *ti, ZoningBorder border, SpriteID color) { + auto b = (uint8)border & 15; + auto tile_sprite = CM_SPR_BORDER_HIGHLIGHT_BASE + _tileh_to_sprite[ti->tileh] * 19; + if (b) { + DrawSelectionSprite(tile_sprite + b - 1, color, ti, 7, FOUNDATION_PART_NORMAL); + } + if (border & ZoningBorder::TOP_CORNER) + DrawSelectionSprite(tile_sprite + 15, color, ti, 7, FOUNDATION_PART_NORMAL); + if (border & ZoningBorder::RIGHT_CORNER) + DrawSelectionSprite(tile_sprite + 16, color, ti, 7, FOUNDATION_PART_NORMAL); + if (border & ZoningBorder::BOTTOM_CORNER) + DrawSelectionSprite(tile_sprite + 17, color, ti, 7, FOUNDATION_PART_NORMAL); + if (border & ZoningBorder::LEFT_CORNER) + DrawSelectionSprite(tile_sprite + 18, color, ti, 7, FOUNDATION_PART_NORMAL); +} + TileHighlight ObjectHighlight::GetTileHighlight(const TileInfo *ti) { TileHighlight th; auto range = this->tiles.equal_range(ti->tile); for (auto t = range.first; t != range.second; t++) { - auto &oth = t->second; - switch (oth.type) { - case ObjectTileHighlight::Type::RAIL_DEPOT: - // case ObjectTileHighlight::Type::RAIL_TRACK: Depot track shouldn't remove foundation - case ObjectTileHighlight::Type::RAIL_STATION: - case ObjectTileHighlight::Type::RAIL_SIGNAL: - case ObjectTileHighlight::Type::RAIL_BRIDGE_HEAD: - case ObjectTileHighlight::Type::RAIL_TUNNEL_HEAD: - case ObjectTileHighlight::Type::ROAD_STOP: - case ObjectTileHighlight::Type::ROAD_DEPOT: - case ObjectTileHighlight::Type::AIRPORT_TILE: - case ObjectTileHighlight::Type::INDUSTRY_TILE: - th.structure_pal = CM_PALETTE_HIDE_SPRITE; - th.highlight_ground_pal = th.highlight_structure_pal = oth.palette; - break; - - default: - break; - } + t->second.SetTileHighlight(th, ti); } return th; } +static void DrawObjectTileHighlight(const TileInfo *ti, const ObjectTileHighlight &oth) { + switch (oth.type) { + case ObjectTileHighlight::Type::RAIL_DEPOT: + DrawTrainDepotSprite(oth.palette, ti, _cur_railtype, oth.u.rail.depot.ddir); + break; + case ObjectTileHighlight::Type::RAIL_TRACK: { + DrawAutorailSelection(ti, (HighLightStyle)oth.u.rail.track, GetSelectionColourByTint(oth.palette)); + break; + } + case ObjectTileHighlight::Type::RAIL_STATION: + DrawTrainStationSprite(oth.palette, ti, _cur_railtype, oth.u.rail.station.axis, oth.u.rail.station.section); + break; + case ObjectTileHighlight::Type::RAIL_SIGNAL: + DrawSignal(oth.palette, ti, _cur_railtype, oth.u.rail.signal.pos, oth.u.rail.signal.type, oth.u.rail.signal.variant); + break; + case ObjectTileHighlight::Type::RAIL_BRIDGE_HEAD: + DrawBridgeHead(oth.palette, ti, _cur_railtype, oth.u.rail.bridge_head.ddir, oth.u.rail.bridge_head.type); + break; + case ObjectTileHighlight::Type::RAIL_TUNNEL_HEAD: + DrawTunnelHead(oth.palette, ti, _cur_railtype, oth.u.rail.tunnel_head.ddir); + break; + case ObjectTileHighlight::Type::ROAD_STOP: + DrawRoadStop(oth.palette, ti, oth.u.road.stop.roadtype, oth.u.road.stop.ddir, oth.u.road.stop.is_truck); + break; + case ObjectTileHighlight::Type::ROAD_DEPOT: + DrawRoadDepot(oth.palette, ti, oth.u.road.depot.roadtype, oth.u.road.depot.ddir); + break; + case ObjectTileHighlight::Type::AIRPORT_TILE: + DrawAirportTile(oth.palette, ti, oth.u.airport_tile.gfx); + break; + case ObjectTileHighlight::Type::INDUSTRY_TILE: + DrawIndustryTile(oth.palette, ti, oth.u.industry_tile.ind_type, oth.u.industry_tile.ind_layout, oth.u.industry_tile.gfx, oth.u.industry_tile.tile_diff); + break; + case ObjectTileHighlight::Type::POINT: + DrawSelectionPoint(oth.palette, ti); + break; + case ObjectTileHighlight::Type::RECT: + DrawTileSelectionRect(ti, oth.palette); + break; + case ObjectTileHighlight::Type::NUMBERED_RECT: { + // TODO NUMBERED_RECT should not be used atm anyway + // DrawTileSelectionRect(ti, oth.palette); + // auto string_id = oth.u.numbered_rect.number ? CM_STR_LAYOUT_NUM : CM_STR_LAYOUT_RANDOM; + // SetDParam(0, oth.u.numbered_rect.number); + // std::string buffer = GetString(string_id); + // auto bb = GetStringBoundingBox(buffer); + // ViewportSign sign; + // sign.width_normal = WidgetDimensions::scaled.fullbevel.left + Align(bb.width, 2) + WidgetDimensions::scaled.fullbevel.right; + // Point pt = RemapCoords2(TileX(ti->tile) * TILE_SIZE + TILE_SIZE / 2, TileY(ti->tile) * TILE_SIZE + TILE_SIZE / 2); + // sign.center = pt.x; + // sign.top = pt.y - bb.height / 2; + + // ViewportAddString(_cur_dpi, ZOOM_LVL_OUT_8X, &sign, + // string_id, STR_NULL, STR_NULL, oth.u.numbered_rect.number, 0, COLOUR_WHITE); + break; + } + case ObjectTileHighlight::Type::BORDER: { + DrawBorderSprites(ti, oth.u.border, oth.palette); + break; + } + default: + break; + } +} + void ObjectHighlight::Draw(const TileInfo *ti) { auto range = this->tiles.equal_range(ti->tile); for (auto t = range.first; t != range.second; t++) { - auto &oth = t->second; - switch (oth.type) { - case ObjectTileHighlight::Type::RAIL_DEPOT: - DrawTrainDepotSprite(oth.palette, ti, _cur_railtype, oth.u.rail.depot.ddir); - break; - case ObjectTileHighlight::Type::RAIL_TRACK: { - DrawAutorailSelection(ti, (HighLightStyle)oth.u.rail.track, GetSelectionColourByTint(oth.palette)); - break; - } - case ObjectTileHighlight::Type::RAIL_STATION: - DrawTrainStationSprite(oth.palette, ti, _cur_railtype, oth.u.rail.station.axis, oth.u.rail.station.section); - break; - case ObjectTileHighlight::Type::RAIL_SIGNAL: - DrawSignal(oth.palette, ti, _cur_railtype, oth.u.rail.signal.pos, oth.u.rail.signal.type, oth.u.rail.signal.variant); - break; - case ObjectTileHighlight::Type::RAIL_BRIDGE_HEAD: - DrawBridgeHead(oth.palette, ti, _cur_railtype, oth.u.rail.bridge_head.ddir, oth.u.rail.bridge_head.type); - break; - case ObjectTileHighlight::Type::RAIL_TUNNEL_HEAD: - DrawTunnelHead(oth.palette, ti, _cur_railtype, oth.u.rail.tunnel_head.ddir); - break; - case ObjectTileHighlight::Type::ROAD_STOP: - DrawRoadStop(oth.palette, ti, oth.u.road.stop.roadtype, oth.u.road.stop.ddir, oth.u.road.stop.is_truck); - break; - case ObjectTileHighlight::Type::ROAD_DEPOT: - DrawRoadDepot(oth.palette, ti, oth.u.road.depot.roadtype, oth.u.road.depot.ddir); - break; - case ObjectTileHighlight::Type::AIRPORT_TILE: - DrawAirportTile(oth.palette, ti, oth.u.airport_tile.gfx); - break; - case ObjectTileHighlight::Type::INDUSTRY_TILE: - DrawIndustryTile(oth.palette, ti, oth.u.industry_tile.ind_type, oth.u.industry_tile.ind_layout, oth.u.industry_tile.gfx, oth.u.industry_tile.tile_diff); - break; - case ObjectTileHighlight::Type::POINT: - DrawSelectionPoint(oth.palette, ti); - break; - case ObjectTileHighlight::Type::RECT: - DrawTileSelectionRect(ti, oth.palette); - break; - case ObjectTileHighlight::Type::NUMBERED_RECT: { - // TODO NUMBERED_RECT should be used atm anyway - // DrawTileSelectionRect(ti, oth.palette); - // auto string_id = oth.u.numbered_rect.number ? CM_STR_LAYOUT_NUM : CM_STR_LAYOUT_RANDOM; - // SetDParam(0, oth.u.numbered_rect.number); - // std::string buffer = GetString(string_id); - // auto bb = GetStringBoundingBox(buffer); - // ViewportSign sign; - // sign.width_normal = WidgetDimensions::scaled.fullbevel.left + Align(bb.width, 2) + WidgetDimensions::scaled.fullbevel.right; - // Point pt = RemapCoords2(TileX(ti->tile) * TILE_SIZE + TILE_SIZE / 2, TileY(ti->tile) * TILE_SIZE + TILE_SIZE / 2); - // sign.center = pt.x; - // sign.top = pt.y - bb.height / 2; - - // ViewportAddString(_cur_dpi, ZOOM_LVL_OUT_8X, &sign, - // string_id, STR_NULL, STR_NULL, oth.u.numbered_rect.number, 0, COLOUR_WHITE); - break; - } - default: - break; - } + DrawObjectTileHighlight(ti, t->second); } // fprintf(stderr, "TILEH DRAW %d %d %d\n", ti->tile, (int)i, (int)this->tiles.size()); } @@ -1456,22 +1632,6 @@ bool CanBuildIndustryOnTileCached(IndustryType type, TileIndex tile) { return (_mz[tile.base()].industry_fund_result == 2); } -void DrawBorderSprites(const TileInfo *ti, ZoningBorder border, SpriteID color) { - auto b = (uint8)border & 15; - auto tile_sprite = CM_SPR_BORDER_HIGHLIGHT_BASE + _tileh_to_sprite[ti->tileh] * 19; - if (b) { - DrawSelectionSprite(tile_sprite + b - 1, color, ti, 7, FOUNDATION_PART_NORMAL); - } - if (border & ZoningBorder::TOP_CORNER) - DrawSelectionSprite(tile_sprite + 15, color, ti, 7, FOUNDATION_PART_NORMAL); - if (border & ZoningBorder::RIGHT_CORNER) - DrawSelectionSprite(tile_sprite + 16, color, ti, 7, FOUNDATION_PART_NORMAL); - if (border & ZoningBorder::BOTTOM_CORNER) - DrawSelectionSprite(tile_sprite + 17, color, ti, 7, FOUNDATION_PART_NORMAL); - if (border & ZoningBorder::LEFT_CORNER) - DrawSelectionSprite(tile_sprite + 18, color, ti, 7, FOUNDATION_PART_NORMAL); -} - SpriteID GetIndustryZoningPalette(TileIndex tile) { if (!IsTileType(tile, MP_INDUSTRY)) return PAL_NONE; Industry *ind = Industry::GetByTile(tile); @@ -1622,7 +1782,16 @@ void CalcCBTownLimitBorder(TileHighlight &th, TileIndex tile, SpriteID border_pa } TileHighlight GetTileHighlight(const TileInfo *ti, TileType tile_type) { - TileHighlight th = _thd.cm.GetTileHighlight(ti);; + TileHighlight th; + auto it = _ap.tiles.find(ti->tile); + if (it != _ap.tiles.end()) { + for (auto &oth : it->second) { + oth.SetTileHighlight(th, ti); + } + return th; + } + + th = _thd.cm.GetTileHighlight(ti);; if (ti->tile == INVALID_TILE || tile_type == MP_VOID) return th; if (_zoning.outer == CHECKTOWNZONES) { auto p = GetTownZoneBorder(ti->tile); @@ -1742,6 +1911,15 @@ 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) { + DrawObjectTileHighlight(ti, oth); + } + return true; + } + _thd.cm.Draw(ti); if (_thd.drawstyle == CM_HT_BLUEPRINT_PLACE) return true; @@ -2064,4 +2242,132 @@ PaletteID GetTreeShadePal(TileIndex tile) { } } +ActivePreview _ap; + +static void ResetVanillaHighlight() { + if (_thd.window_class != WC_INVALID) { + /* Undo clicking on button and drag & drop */ + Window *w = _thd.GetCallbackWnd(); + /* Call the abort function, but set the window class to something + * that will never be used to avoid infinite loops. Setting it to + * the 'next' window class must not be done because recursion into + * this function might in some cases reset the newly set object to + * place or not properly reset the original selection. */ + _thd.window_class = WC_INVALID; + if (w != nullptr) { + w->OnPlaceObjectAbort(); + CloseWindowById(WC_TOOLTIPS, 0); + } + } + + /* Mark the old selection dirty, in case the selection shape or colour changes */ + if ((_thd.drawstyle & HT_DRAG_MASK) != HT_NONE) SetSelectionTilesDirty(); + + SetTileSelectSize(1, 1); + + _thd.make_square_red = false; +} + +void SetActivePreview(up &&preview) { + ResetVanillaHighlight(); + ResetActivePreview(); + _ap.preview = std::move(preview); +} + +void ResetActivePreview() { + for (auto &[t, l] : _ap.tiles) { + MarkTileDirtyByTile(t); + } + _ap.preview = nullptr; + _ap.tiles = {}; +} + +void UpdateActivePreview() { + if (_ap.preview == nullptr) return; + 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)); + } + 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); + MarkTileDirtyByTile(t); + } +} + +bool _prev_left_button_down = false; +// const Window *_click_window = nullptr; +bool _keep_mouse_click = false; +// const Window *_keep_mouse_window; + +bool HandleMouseMove() { + bool changed = _left_button_down != _prev_left_button_down; + _prev_left_button_down = _left_button_down; + // Window *w = FindWindowFromPt(_cursor.pos.x, _cursor.pos.y); + bool released = !_left_button_down && changed && _keep_mouse_click; + if (!_left_button_down) _keep_mouse_click = false; + + if (_ap.preview == 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(); + if (_left_button_down) { + if (changed && _ap.preview->HandleMousePress()) { + _keep_mouse_click = true; + } + if (_keep_mouse_click) return true; + } + if (released) { + _ap.preview->HandleMouseRelease(); + } + return false; +} + +bool HandleMouseClick(Viewport *vp, bool double_click) { + if (_ap.preview == 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); +} + +bool HandlePlacePushButton(Window *w, WidgetID widget, up preview) { + if (w->IsWidgetDisabled(widget)) return false; + + if (_settings_client.sound.click_beep) SndPlayFx(SND_15_BEEP); + w->SetDirty(); + + if (w->IsWidgetLowered(widget)) { + ResetObjectToPlace(); + return false; + } + + w->LowerWidget(widget); + + auto icon = preview->GetCursor(); + if ((icon & ANIMCURSOR_FLAG) != 0) { + SetAnimatedMouseCursor(_animcursors[icon & ~ANIMCURSOR_FLAG]); + } else { + SetMouseCursor(icon, PAL_NONE); + } + citymania::SetActivePreview(std::move(preview)); + _thd.window_class = w->window_class; + _thd.window_number = w->window_number; + + return true; + +} + + } // namespace citymania diff --git a/src/citymania/cm_highlight.hpp b/src/citymania/cm_highlight.hpp index 9c0b7faa26..1cf709c895 100644 --- a/src/citymania/cm_highlight.hpp +++ b/src/citymania/cm_highlight.hpp @@ -1,6 +1,7 @@ #ifndef CITYMANIA_HIGHLIGHT_HPP #define CITYMANIA_HIGHLIGHT_HPP +#include "cm_commands.hpp" #include "cm_highlight_type.hpp" #include "../core/enum_type.hpp" @@ -56,6 +57,14 @@ PaletteID GetTreeShadePal(TileIndex tile); void RotateAutodetection(); void ResetRotateAutodetection(); +void ResetActivePreview(); +void SetActivePreview(up &&preview); +void UpdateActivePreview(); + +bool HandlePlacePushButton(Window *w, WidgetID widget, up preview); +bool HandleMouseMove(); +bool HandleMouseClick(Viewport *vp, bool double_click); + } // namespace citymania #endif diff --git a/src/citymania/cm_highlight_type.hpp b/src/citymania/cm_highlight_type.hpp index 0655bb9221..34ed8f1fa0 100644 --- a/src/citymania/cm_highlight_type.hpp +++ b/src/citymania/cm_highlight_type.hpp @@ -16,11 +16,18 @@ #include #include +#include +#include #include +#include "cm_command_type.hpp" + namespace citymania { +typedef std::function(TileIndex start_tile, TileIndex end_tile)> HighlightGenerator; + + enum ZoningBorder: uint8 { NONE = 0, TOP_LEFT = 1, @@ -81,7 +88,7 @@ public: class ObjectTileHighlight { public: - enum class Type { + enum class Type : uint8_t { BEGIN = 0, RAIL_DEPOT = BEGIN, RAIL_TRACK, @@ -97,6 +104,9 @@ public: POINT, RECT, NUMBERED_RECT, + BORDER, + TINT, + STRUCT_TINT, END, }; @@ -152,9 +162,11 @@ public: struct { uint32 number; } numbered_rect; + ZoningBorder border; } u; ObjectTileHighlight(Type type, SpriteID palette): type{type}, palette{palette} {} + static ObjectTileHighlight make_rail_depot(SpriteID palette, DiagDirection ddir); static ObjectTileHighlight make_rail_track(SpriteID palette, Track track); static ObjectTileHighlight make_rail_station(SpriteID palette, Axis axis, byte section); @@ -169,6 +181,12 @@ public: static ObjectTileHighlight make_point(SpriteID palette); static ObjectTileHighlight make_rect(SpriteID palette); static ObjectTileHighlight make_numbered_rect(SpriteID palette, uint32 number); + static ObjectTileHighlight make_border(SpriteID palette, ZoningBorder border); + static ObjectTileHighlight make_tint(SpriteID palette); + static ObjectTileHighlight make_struct_tint(SpriteID palette); + + bool operator==(const ObjectTileHighlight &oh) const; + bool SetTileHighlight(TileHighlight &th, const TileInfo *ti) const; }; @@ -282,7 +300,7 @@ public: class ObjectHighlight { public: - enum class Type { + enum class Type : byte { NONE = 0, RAIL_DEPOT = 1, RAIL_STATION = 2, @@ -351,6 +369,33 @@ public: void MarkDirty(); }; +class Preview { +public: + typedef std::map> 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 CursorID GetCursor() = 0; + virtual void OnStationRemoved(const Station* /* station */) {}; +}; + +// enum class ActiveHighlightState { +// None, +// Place, +// DragStart, +// DragStop, +// }; + +struct ActivePreview { + up preview; + Preview::TileMap tiles; +}; + +extern ActivePreview _ap; } // namespace citymania diff --git a/src/citymania/cm_station_gui.cpp b/src/citymania/cm_station_gui.cpp index a2aff5801e..b5f0b1f0e3 100644 --- a/src/citymania/cm_station_gui.cpp +++ b/src/citymania/cm_station_gui.cpp @@ -2,6 +2,8 @@ #include "cm_station_gui.hpp" +#include "cm_highlight.hpp" +#include "cm_highlight_type.hpp" #include "cm_hotkeys.hpp" #include "cm_commands.hpp" @@ -28,8 +30,13 @@ #include "../window_gui.h" #include "../zoom_type.h" #include "../zoom_func.h" +#include "cm_type.hpp" +#include "generated/cm_gen_commands.hpp" #include +#include + +bool _remove_button_clicked; // replace vanilla static vars extern const Station *_viewport_highlight_station; extern TileHighlightData _thd; @@ -42,6 +49,8 @@ extern AirportClassID _selected_airport_class; extern int _selected_airport_index; extern byte _selected_airport_layout; extern RailType _cur_railtype; // rail_gui.cpp +extern RoadType _cur_roadtype; // road_gui.cpp +extern void GetStationLayout(byte *layout, uint numtracks, uint plat_len, const StationSpec *statspec); struct RailStationGUISettings { Axis orientation; ///< Currently selected rail station orientation @@ -65,18 +74,11 @@ extern RoadStopGUISettings _roadstop_gui_settings; namespace citymania { StationBuildingStatus _station_building_status = StationBuildingStatus::NEW; -const Station *_station_to_join = nullptr; const Station *_highlight_station_to_join = nullptr; TileArea _highlight_join_area; -// void SetStationTileSelectSize(int w, int h, int catchment) { -// _station_select.w = w; -// _station_select.h = h; -// _station_select.catchment = catchment; -// } - bool UseImprovedStationJoin() { - return _settings_client.gui.cm_use_improved_station_join && _settings_game.station.distant_join_stations; + return _settings_client.gui.cm_use_improved_station_join && _settings_game.station.distant_join_stations && _settings_game.station.adjacent_stations; } void SetStationBiildingStatus(StationBuildingStatus status) { @@ -109,19 +111,19 @@ static void UpdateHiglightJoinArea(const Station *station) { _highlight_join_area.tile = INVALID_TILE; return; } - auto &r = _station_to_join->rect; - auto d = (int)_settings_game.station.station_spread - 1; - TileArea ta( - TileXY(std::max(r.right - d, 0), - std::max(r.bottom - d, 0)), - TileXY(std::min(r.left + d, Map::SizeX() - 1), - std::min(r.top + d, Map::SizeY() - 1)) - ); - if (_highlight_join_area.tile == ta.tile && - _highlight_join_area.w == ta.w && - _highlight_join_area.h == ta.h) return; + // auto &r = _station_to_join->rect; + // auto d = (int)_settings_game.station.station_spread - 1; + // TileArea ta( + // TileXY(std::max(r.right - d, 0), + // std::max(r.bottom - d, 0)), + // TileXY(std::min(r.left + d, Map::SizeX() - 1), + // std::min(r.top + d, Map::SizeY() - 1)) + // ); + // if (_highlight_join_area.tile == ta.tile && + // _highlight_join_area.w == ta.w && + // _highlight_join_area.h == ta.h) return; MarkTileAreaDirty(_highlight_join_area); - _highlight_join_area = ta; + // _highlight_join_area = ta; MarkTileAreaDirty(_highlight_join_area); } @@ -147,8 +149,8 @@ void SetHighlightStationToJoin(const Station *station, bool with_area) { void OnStationTileSetChange(const Station *station, bool /* adding */, StationType /* type */) { if (station == _highlight_station_to_join) { - if (_highlight_join_area.tile != INVALID_TILE) - UpdateHiglightJoinArea(_station_to_join); + // if (_highlight_join_area.tile != INVALID_TILE) + // UpdateHiglightJoinArea(_station_to_join); if (_settings_client.gui.station_show_coverage) MarkCoverageAreaDirty(_highlight_station_to_join); } @@ -168,15 +170,6 @@ void OnStationPartBuilt(const Station *station) { CheckRedrawStationCoverage(); } -void OnStationRemoved(const Station *station) { - if (_last_built_station == station) _last_built_station = nullptr; - if (_station_to_join == station) { - // TODO MarkJoinHighlight - MarkCoverageAreaDirty(station); - _station_to_join = nullptr; - } -} - const Station *CheckClickOnDeadStationSign() { int x = _cursor.pos.x; @@ -231,7 +224,7 @@ void JoinAndBuild(Tcommand command, Tcallback *callback) { command.with_callback([] (bool res)->bool { if (!res) return false; - _station_to_join = _last_built_station; + // _station_to_join = _last_built_station; return true; }).post(callback); } @@ -318,7 +311,7 @@ DiagDirection AutodetectRoadObjectDirection(TileIndex tile, Point pt, RoadType r if (HasExactlyOneBit(bits)) { return RoadBitsToDiagDir(bits); } - if (bits == ROAD_NONE){ + if (bits == ROAD_NONE) { bits = ROAD_ALL; } RoadBits frac_bits = DiagDirToRoadBits(TileFractCoordsToDiagDir(pt)); @@ -462,35 +455,6 @@ void PlaceAirport(TileIndex tile) { JoinAndBuild(c, CcBuildAirport); } -static void FindStationsAroundSelection(const TileArea &location) -{ - /* Extended area by one tile */ - int x = TileX(location.tile); - int y = TileY(location.tile); - - TileArea ta(TileXY(std::max(0, x - 1), std::max(0, y - 1)), TileXY(std::min(Map::MaxX() - 1, x + location.w + 1), std::min(Map::MaxY() - 1, y + location.h + 1))); - - Station *adjacent = nullptr; - - /* Direct loop instead of FindStationsAroundTiles as we are not interested in catchment area */ - for (auto tile : ta) { - if (IsTileType(tile, MP_STATION) && GetTileOwner(tile) == _local_company) { - Station *st = Station::GetByTile(tile); - if (st == nullptr) continue; - - int tx = TileX(tile); - int ty = TileY(tile); - bool is_corner = ((tx == x - 1 || tx == x + location.w + 1) && (ty == y - 1 || ty == y + location.h + 1)); - - if (adjacent && is_corner) continue; - adjacent = st; - if (!is_corner) break; - } - } - SetHighlightStationToJoin(adjacent, false); - _station_building_status = (adjacent == nullptr ? StationBuildingStatus::NEW : StationBuildingStatus::JOIN); -} - bool CheckRedrawStationCoverage() { // static bool last_ctrl_pressed = false; static TileArea last_location; @@ -512,30 +476,30 @@ bool CheckRedrawStationCoverage() { if (IsTileType(location.tile, MP_STATION) && GetTileOwner(location.tile) == _local_company) st = Station::GetByTile(location.tile); - SetHighlightStationToJoin(st, _station_to_join && st == _station_to_join); + // SetHighlightStationToJoin(st, _station_to_join && st == _station_to_join); _station_building_status = (st == nullptr ? StationBuildingStatus::NEW : StationBuildingStatus::JOIN); } else { - if (_station_to_join) { - SetHighlightStationToJoin(_station_to_join, true); - _station_building_status = StationBuildingStatus::JOIN; - } else { - FindStationsAroundSelection(location); - } + // if (_station_to_join) { + // SetHighlightStationToJoin(_station_to_join, true); + // _station_building_status = StationBuildingStatus::JOIN; + // } else { + // FindStationsAroundSelection(location); + // } } return true; } -void SelectStationToJoin(const Station *station) { - if (_station_to_join == station) - _station_to_join = nullptr; - else - _station_to_join = station; +void SelectStationToJoin(const Station *) { + // if (_station_to_join == station) + // _station_to_join = nullptr; + // else + // _station_to_join = station; CheckRedrawStationCoverage(); } void AbortStationPlacement() { - _station_to_join = nullptr; + // _station_to_join = nullptr; SetHighlightStationToJoin(nullptr, false); } @@ -678,4 +642,554 @@ std::string GetStationCoverageProductionText(TileIndex tile, int w, int h, int r return s.str(); } + +// ---- NEw code + +StationID _station_to_join = INVALID_STATION; +std::chrono::time_point _station_to_join_selected; + +void OnStationRemoved(const Station *station) { + if (_last_built_station == station) _last_built_station = nullptr; + if (_station_to_join == station->index) { + _station_to_join = INVALID_STATION; + } + if (_ap.preview != nullptr) _ap.preview->OnStationRemoved(station); +} + +static void AddAreaRectTiles(Preview::TileMap &tiles, TileArea area, SpriteID palette) { + if (area.w == 0 || area.h == 0) return; + + if (area.w == 1 && area.h == 1) { + tiles[area.tile].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::FULL)); + return; + } + auto sx = TileX(area.tile), sy = TileY(area.tile); + auto ex = sx + area.w - 1, ey = sy + area.h - 1; + // NOTE: Doesn't handle one-tile width/height separately but relies on border overlapping + tiles[TileXY(sx, sy)].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::TOP_LEFT | ZoningBorder::TOP_RIGHT)); + for (auto x = sx + 1; x < ex; x++) + tiles[TileXY(x, sy)].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::TOP_LEFT)); + tiles[TileXY(ex, sy)].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::TOP_LEFT | ZoningBorder::BOTTOM_LEFT)); + for (auto y = sy + 1; y < ey; y++) { + tiles[TileXY(sx, y)].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::TOP_RIGHT)); + for (auto x = sx + 1; x < ex; x++) { + tiles[TileXY(x, y)].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::NONE)); + } + tiles[TileXY(ex, y)].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::BOTTOM_LEFT)); + } + tiles[TileXY(sx, ey)].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::TOP_RIGHT | ZoningBorder::BOTTOM_RIGHT)); + for (auto x = sx + 1; x < ex; x++) + tiles[TileXY(x, ey)].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::BOTTOM_RIGHT)); + tiles[TileXY(ex, ey)].push_back(ObjectTileHighlight::make_border(palette, ZoningBorder::BOTTOM_LEFT | ZoningBorder::BOTTOM_RIGHT)); +} + +// copied from cm_blueprint.cpp +template +void IterateStation(TileIndex start_tile, Axis axis, byte numtracks, byte plat_len, Func visitor) { + auto plat_delta = (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); + auto track_delta = (axis == AXIS_Y ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); + TileIndex tile_track = start_tile; + do { + TileIndex tile = tile_track; + int w = plat_len; + do { + visitor(tile); + tile += plat_delta; + } while (--w); + tile_track += track_delta; + } while (--numtracks); +} + +void AddJoinAreaTiles(Preview::TileMap &tiles, StationID station_id) { + auto station = Station::GetIfValid(station_id); + if (station == nullptr) return; + + auto &r = station->rect; + auto d = (int)_settings_game.station.station_spread - 1; + TileArea ta( + TileXY(std::max(r.right - d, 0), + std::max(r.bottom - d, 0)), + TileXY(std::min(r.left + d, Map::SizeX() - 1), + std::min(r.top + d, Map::SizeY() - 1)) + ); + + AddAreaRectTiles(tiles, ta, CM_PALETTE_TINT_CYAN); +} + +bool RailStationPreview::IsDragDrop() const { + return _settings_client.gui.station_dragdrop; +} + +CursorID RailStationPreview::GetCursor() const { + return SPR_CURSOR_RAIL_STATION; +} + +TileArea RailStationPreview::GetArea(bool remove_mode) const { + if (this->IsDragDrop() || remove_mode) return {this->GetStartTile(), this->cur_tile}; + + if (_railstation.orientation == AXIS_X) return {this->cur_tile, _settings_client.gui.station_platlength, _settings_client.gui.station_numtracks}; + return {this->cur_tile, _settings_client.gui.station_numtracks, _settings_client.gui.station_platlength}; +} + +up RailStationPreview::GetCommand(bool adjacent, StationID join_to) const { + auto ta = this->GetArea(false); + auto start_tile = ta.tile; + auto numtracks = ta.w; + auto platlength = ta.h; + if (_railstation.orientation == AXIS_X) Swap(numtracks, platlength); + + auto res = make_up( + start_tile, + _cur_railtype, + _railstation.orientation, + numtracks, + platlength, + _railstation.station_class, + _railstation.station_type, + join_to, + adjacent + ); + res->with_error(STR_ERROR_CAN_T_BUILD_RAILROAD_STATION); + return res; +} + +up RailStationPreview::GetRemoveCommand() const { + auto res = make_up( + this->GetStartTile(), + this->cur_tile, + !citymania::_fn_mod + ); + res->with_error(STR_ERROR_CAN_T_REMOVE_PART_OF_STATION); + return res; +} + +bool RailStationPreview::Execute(up cmd, bool remove_mode) const { + if (remove_mode) return cmd->post(&CcPlaySound_CONSTRUCTION_RAIL); + else return cmd->post(&CcStation); +} + +void RailStationPreview::AddPreviewTiles(Preview::TileMap &tiles, SpriteID palette) const { + auto cmd = this->GetCommand(true, NEW_STATION); + auto cmdt = dynamic_cast(cmd.get()); + if (cmdt == nullptr) return; + + if (palette == PAL_NONE) palette = cmd->test().Succeeded() ? CM_PALETTE_TINT_WHITE : CM_PALETTE_TINT_RED_DEEP; + + std::vector layouts(cmdt->numtracks * cmdt->plat_len); + byte *layout_ptr = layouts.data(); + GetStationLayout(layout_ptr, cmdt->numtracks, cmdt->plat_len, nullptr); + IterateStation(cmdt->tile_org, cmdt->axis, cmdt->numtracks, cmdt->plat_len, + [&](TileIndex t) { + byte layout = *layout_ptr++; + tiles[t].push_back(ObjectTileHighlight::make_rail_station(palette, cmdt->axis, layout & ~1)); + } + ); +} + +OverlayParams RailStationPreview::GetOverlayParams() const { + return {this->GetArea(false), CA_TRAIN, SCT_ALL}; +} + +bool RoadStationPreview::IsDragDrop() const { + return true; +} + +CursorID RoadStationPreview::GetCursor() const { + return SPR_CURSOR_BUS_STATION; + // return SPR_CURSOR_TRUCK_STATION; +} + +TileArea RoadStationPreview::GetArea(bool /* remove_mode */) const { + return {this->GetStartTile(), this->cur_tile}; +} + +extern DiagDirection AddAutodetectionRotation(DiagDirection ddir); // cm_highlight.cpp + +void RoadStationPreview::Update(Point pt, TileIndex tile) { + if (pt.x == -1) return; + + auto ddir = _roadstop_gui_settings.orientation; + auto area = this->GetArea(false); + if (ddir >= DIAGDIR_END && ddir < STATIONDIR_AUTO) { + // When placed on road autorotate anyway + if (ddir == STATIONDIR_X) { + if (!CheckDriveThroughRoadStopDirection(area, ROAD_X)) + ddir = STATIONDIR_Y; + } else { + if (!CheckDriveThroughRoadStopDirection(area, ROAD_Y)) + ddir = STATIONDIR_X; + } + } else if (ddir == STATIONDIR_AUTO) { + ddir = AddAutodetectionRotation(AutodetectRoadObjectDirection(tile, pt, _cur_roadtype)); + } else if (ddir == STATIONDIR_AUTO_XY) { + ddir = AddAutodetectionRotation(AutodetectDriveThroughRoadStopDirection(area, pt, _cur_roadtype)); + } + this->ddir = ddir; +} + +up RoadStationPreview::GetCommand(bool adjacent, StationID join_to) const { + auto area = this->GetArea(false); + DiagDirection ddir = this->ddir; + bool drive_through = this->ddir >= DIAGDIR_END; + if (drive_through) ddir = static_cast(this->ddir - DIAGDIR_END); // Adjust picker result to actual direction. + RoadStopClassID spec_class = _roadstop_gui_settings.roadstop_class; + uint16_t spec_index = _roadstop_gui_settings.roadstop_type; + + auto res = make_up( + area.tile, + area.w, + area.h, + this->stop_type, + drive_through, + ddir, + _cur_roadtype, + spec_class, + spec_index, + join_to, + adjacent + ); + + return res; +} + +up RoadStationPreview::GetRemoveCommand() const { + auto area = this->GetArea(false); + auto res = make_up( + area.tile, + area.w, + area.h, + this->stop_type, + citymania::_fn_mod + ); + auto rti = GetRoadTypeInfo(_cur_roadtype); + res->with_error(rti->strings.err_remove_station[this->stop_type]); + return res; +} + +bool RoadStationPreview::Execute(up cmd, bool remove_mode) const { + if (remove_mode) return cmd->post(&CcPlaySound_CONSTRUCTION_OTHER); + else return cmd->post(&CcRoadStop); +} + +void RoadStationPreview::AddPreviewTiles(Preview::TileMap &tiles, SpriteID palette) const { + auto cmd = this->GetCommand(true, NEW_STATION); + auto cmdt = dynamic_cast(cmd.get()); + if (cmdt == nullptr) return; + + if (palette == PAL_NONE) palette = cmd->test().Succeeded() ? CM_PALETTE_TINT_WHITE : CM_PALETTE_TINT_RED_DEEP; + + for (TileIndex t : this->GetArea(false)) { + auto ddir = cmdt->ddir; + if (cmdt->is_drive_through) ddir = ddir + DIAGDIR_END; + tiles[t].push_back(ObjectTileHighlight::make_road_stop( + palette, + cmdt->rt, + ddir, + cmdt->stop_type == ROADSTOP_TRUCK, + cmdt->spec_class, + cmdt->spec_index + )); + } +} + +OverlayParams RoadStationPreview::GetOverlayParams() const { + return { + this->GetArea(false), + this->stop_type == ROADSTOP_TRUCK ? CA_TRUCK : CA_BUS, + this->stop_type == ROADSTOP_TRUCK ? SCT_NON_PASSENGERS_ONLY : SCT_PASSENGERS_ONLY + }; +} + +void StationPreviewBase::AddAreaTiles(Preview::TileMap &tiles, bool add_current, bool show_join_area) { + Station *st_join = Station::GetIfValid(this->station_to_join); + std::set join_area; + + if (show_join_area && st_join != nullptr) { + AddJoinAreaTiles(tiles, st_join->index); + for (auto t : tiles) join_area.insert(t.first); + } + + if (this->show_coverage && st_join != nullptr) { + // Add joining station coverage + for (auto t : st_join->catchment_tiles) { + auto pal = join_area.find(t) != join_area.end() ? CM_PALETTE_TINT_CYAN_WHITE : CM_PALETTE_TINT_WHITE; + tiles[t].push_back(ObjectTileHighlight::make_tint(pal)); + } + } + + if (this->show_coverage && add_current) { + // Add current station coverage + auto rad = CA_UNMODIFIED; + if (_settings_game.station.modified_catchment) rad = CA_TRAIN; + auto area = this->type->GetArea(false); + area.Expand(rad); + area.ClampToMap(); + for (auto t : area) { + auto pal = join_area.find(t) != join_area.end() ? CM_PALETTE_TINT_CYAN_WHITE : CM_PALETTE_TINT_WHITE; + tiles[t].push_back(ObjectTileHighlight::make_tint(pal)); + } + } + + if (st_join != nullptr) { + TileArea ta(TileXY(st_join->rect.left, st_join->rect.top), TileXY(st_join->rect.right, st_join->rect.bottom)); + for (TileIndex t : ta) { + if (!IsTileType(t, MP_STATION) || GetStationIndex(t) != st_join->index) continue; + tiles[t].push_back(ObjectTileHighlight::make_struct_tint(CM_PALETTE_TINT_BLUE)); + } + } +} + +void StationPreviewBase::Update(Point pt, TileIndex tile) { + if (tile != INVALID_TILE) this->type->cur_tile = tile; + this->show_coverage = _settings_client.gui.station_show_coverage; + this->adjacent_stations = _settings_game.station.adjacent_stations; + this->remove_mode = false; + if (_remove_button_clicked) { + this->remove_mode = true; + this->keep_rail = !_fn_mod; + } else if (!this->type->IsDragDrop()) { + this->type->start_tile = INVALID_TILE; + } + this->type->Update(pt, tile); +} + +bool StationPreviewBase::HandleMousePress() { + if (!IsValidTile(this->type->cur_tile)) return false; + + if (this->remove_mode || this->type->IsDragDrop()) { + this->type->start_tile = this->type->cur_tile; + return true; + } + + this->Execute(); + return true; +} + +void StationPreviewBase::HandleMouseRelease() { + if (!IsValidTile(this->type->cur_tile)) return; + + if (this->type->start_tile != INVALID_TILE) { + this->Execute(); + this->type->start_tile = INVALID_TILE; + } +} + +std::vector> StationPreviewBase::GetOverlayData() { + if (this->remove_mode) return {}; + + std::vector> res; + auto params = this->type->GetOverlayParams(); + + if (!_settings_game.station.modified_catchment) params.radius = CA_UNMODIFIED; + auto production = citymania::GetProductionAroundTiles(params.area.tile, params.area.w, params.area.h, params.radius); + bool has_header = false; + for (CargoID i = 0; i < NUM_CARGO; i++) { + if (production[i] == 0) continue; + + switch (params.coverage_type) { + case SCT_PASSENGERS_ONLY: if (!IsCargoInClass(i, CC_PASSENGERS)) continue; break; + case SCT_NON_PASSENGERS_ONLY: if (IsCargoInClass(i, CC_PASSENGERS)) continue; break; + case SCT_ALL: break; + default: NOT_REACHED(); + } + + const CargoSpec *cs = CargoSpec::Get(i); + if (cs == nullptr) continue; + + if (!has_header) { + res.emplace_back(PAL_NONE, GetString(CM_STR_BUILD_INFO_OVERLAY_STATION_SUPPLIES)); + has_header = true; + } + SetDParam(0, i); + SetDParam(1, production[i] >> 8); + res.emplace_back(cs->GetCargoIcon(), GetString(CM_STR_BUILD_INFO_OVERLAY_STATION_CARGO)); + } + return res; +} + +up StationPreviewBase::GetCommand(bool adjacent, StationID join_to) { + if (this->remove_mode) return this->type->GetRemoveCommand(); + return this->type->GetCommand(adjacent, join_to); +} + +Preview::TileMap VanillaStationPreview::GetTiles() { + Preview::TileMap tiles; + + if (!IsValidTile(this->type->cur_tile)) return tiles; + + if (this->remove_mode) { + AddAreaRectTiles(tiles, this->type->GetArea(true), CM_PALETTE_TINT_RED_DEEP); + return tiles; + } + + this->AddAreaTiles(tiles, true, false); + this->type->AddPreviewTiles(tiles, this->palette); + + return tiles; +} + +void VanillaStationPreview::Update(Point pt, TileIndex tile) { + StationPreviewBase::Update(pt, tile); + this->palette = CM_PALETTE_TINT_WHITE; + + if (this->remove_mode) return; + if (this->selected_station_to_join != INVALID_STATION) { + this->station_to_join = this->selected_station_to_join; + return; + } + + if (!IsValidTile(this->type->cur_tile)) return; + this->station_to_join = INVALID_STATION; + auto area = this->type->GetArea(false); + area.Expand(1); + area.ClampToMap(); + for (auto tile : area) { + if (IsTileType(tile, MP_STATION) && GetTileOwner(tile) == _local_company) { + Station *st = Station::GetByTile(tile); + if (st == nullptr || st->index == this->station_to_join) continue; + if (this->station_to_join != INVALID_STATION) { + this->station_to_join = INVALID_STATION; + this->palette = CM_PALETTE_TINT_YELLOW; + break; + } + this->station_to_join = st->index; + // TODO check for command to return multiple? but also check each to + // see if they can be built + // if (this->GetCommand(true, st->index)->test().Succeeded()) { + // if (this->station_to_join != INVALID_STATION) { + // this->station_to_join = INVALID_STATION; + // this->palette = CM_PALETTE_TINT_YELLOW; + // break; + // } else this->station_to_join = st->index; + // } + } + } + if (this->station_to_join == INVALID_STATION && !this->GetCommand(true, NEW_STATION)->test().Succeeded()) + this->palette = CM_PALETTE_TINT_RED_DEEP; +} + +void VanillaStationPreview::Execute() { + if (this->remove_mode) { + this->type->Execute(this->type->GetRemoveCommand(), true); + return; + } + auto proc = [type=this->type](bool test, StationID to_join) -> bool { + auto cmd = type->GetCommand(_fn_mod, to_join); + if (test) return cmd->test().Succeeded(); + return type->Execute(std::move(cmd), false); + }; + ShowSelectStationIfNeeded(this->type->GetArea(false), proc); +} + +void VanillaStationPreview::OnStationRemoved(const Station *station) { + if (this->station_to_join == station->index) this->station_to_join = INVALID_STATION; + if (this->selected_station_to_join == station->index) this->station_to_join = INVALID_STATION; +} + +StationPreview::StationPreview(sp type) + :StationPreviewBase{type} +{ + auto seconds_since_selected = std::chrono::duration_cast(std::chrono::system_clock::now() - _station_to_join_selected).count(); + if (seconds_since_selected < 30) this->station_to_join = _station_to_join; + else this->station_to_join = INVALID_STATION; +} + +StationPreview::~StationPreview() { + _station_to_join_selected = std::chrono::system_clock::now(); +} + +up StationPreview::GetCommand() { + if (this->select_mode) return nullptr; + + auto res = StationPreviewBase::GetCommand(true, this->station_to_join); + if (this->remove_mode) return res; + + res->with_callback([] (bool res) -> bool { + if (!res) return false; + if (_last_built_station == nullptr) return false; + _station_to_join = _last_built_station->index; + _station_to_join_selected = std::chrono::system_clock::now(); + auto p = dynamic_cast(_ap.preview.get()); + if (p == nullptr) return false; + p->station_to_join = _last_built_station->index; + return true; + }); + return res; +} + +Preview::TileMap StationPreview::GetTiles() { + Preview::TileMap tiles; + + if (!IsValidTile(this->type->cur_tile)) return tiles; + + if (this->remove_mode) { + AddAreaRectTiles(tiles, this->type->GetArea(true), CM_PALETTE_TINT_RED_DEEP); + return tiles; + } + + this->AddAreaTiles(tiles, !this->select_mode, true); + + if (this->select_mode) { + tiles[this->type->cur_tile].push_back(ObjectTileHighlight::make_border(CM_PALETTE_TINT_BLUE, ZoningBorder::FULL)); + return tiles; + } + + this->type->AddPreviewTiles(tiles, PAL_NONE); + + return tiles; +} + +void StationPreview::Update(Point pt, TileIndex tile) { + this->select_mode = false; + StationPreviewBase::Update(pt, tile); + if (!this->remove_mode && _fn_mod) { + this->select_mode = true; + this->type->start_tile = INVALID_TILE; + } +} + +bool StationPreview::HandleMousePress() { + if (!IsValidTile(this->type->cur_tile)) return false; + + if (this->select_mode) { + if (IsTileType(this->type->cur_tile, MP_STATION)) { + auto st = Station::GetByTile(this->type->cur_tile); + this->station_to_join = st->index; + _station_to_join = this->station_to_join; + _station_to_join_selected = std::chrono::system_clock::now(); + } else { + this->station_to_join = INVALID_STATION; + _station_to_join = INVALID_STATION; + } + return true; + } + + return StationPreviewBase::HandleMousePress(); +} + +void StationPreview::Execute() { + this->type->Execute(std::move(this->GetCommand()), this->remove_mode); +} + +void StationPreview::OnStationRemoved(const Station *station) { + if (this->station_to_join == station->index) this->station_to_join = INVALID_STATION; +} + +void SetSelectedStationToJoin(StationID station_id) { + auto p = dynamic_cast(_ap.preview.get()); + if (p == nullptr) return; + p->selected_station_to_join = station_id; + UpdateActivePreview(); +} + +bool HandleStationPlacePushButton(Window *w, WidgetID widget, sp type) { + up preview; + if (citymania::UseImprovedStationJoin()) { + preview = make_up(type); + } else { + preview = make_up(type); + } + return citymania::HandlePlacePushButton(w, widget, std::move(preview)); +} + } // namespace citymania diff --git a/src/citymania/cm_station_gui.hpp b/src/citymania/cm_station_gui.hpp index b1739e55f4..0f6c96923f 100644 --- a/src/citymania/cm_station_gui.hpp +++ b/src/citymania/cm_station_gui.hpp @@ -1,6 +1,8 @@ #ifndef CM_STATION_GUI_HPP #define CM_STATION_GUI_HPP +#include "cm_highlight_type.hpp" + #include "../core/geometry_type.hpp" #include "../command_type.h" #include "../road_type.h" @@ -15,6 +17,15 @@ const DiagDirection STATIONDIR_Y = (DiagDirection)((uint)DIAGDIR_END + 1); const DiagDirection STATIONDIR_AUTO = (DiagDirection)((uint)DIAGDIR_END + 2); const DiagDirection STATIONDIR_AUTO_XY = (DiagDirection)((uint)DIAGDIR_END + 3); +struct RailStationGUISettings { + Axis orientation; ///< Currently selected rail station orientation + + bool newstations; ///< Are custom station definitions available? + StationClassID station_class; ///< Currently selected custom station class (if newstations is \c true ) + uint16_t station_type; ///< %Station type within the currently selected custom station class (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, @@ -46,6 +57,135 @@ 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); + + +struct OverlayParams { + TileArea area; + CatchmentArea radius; + StationCoverageType coverage_type; +}; + + +class PreviewStationType { +public: + 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 GetCommand(bool adjacent, StationID join_to) const =0; + virtual up GetRemoveCommand() const =0; + virtual void AddPreviewTiles(Preview::TileMap &tiles, SpriteID palette) const =0; + virtual bool Execute(up 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 GetCommand(bool adjacent, StationID join_to) const override; + up GetRemoveCommand() const override; + void AddPreviewTiles(Preview::TileMap &tiles, SpriteID palette) const override; + bool Execute(up 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() {}; + + // RailPreviewStation(RailStationGUISettings &settings) :settings{settings} {} + bool IsDragDrop() const override; + CursorID GetCursor() const override; + TileArea GetArea(bool remove_mode) const override; + void Update(Point pt, TileIndex tile) override; + up GetCommand(bool adjacent, StationID join_to) const override; + up GetRemoveCommand() const override; + void AddPreviewTiles(Preview::TileMap &tiles, SpriteID palette) const override; + bool Execute(up cmd, bool remove_mode) const override; + OverlayParams GetOverlayParams() const override; +}; + + +class StationPreviewBase : public Preview { +protected: + sp type; + bool remove_mode = false; + bool keep_rail = true; // whether to keep rail in remove mode + StationID station_to_join = INVALID_STATION; + bool adjacent_stations = false; + bool show_coverage = true; + + void AddAreaTiles(Preview::TileMap &tiles, bool add_current, bool show_join_area); + virtual void Execute() = 0; + up GetCommand(bool adjacent, StationID join_to); + void AddStationPreview(Preview::TileMap &tiles, SpriteID palette); + +public: + StationPreviewBase(sp type) :type{type} {}; + CursorID GetCursor() override { return this->type->GetCursor(); }; + void Update(Point pt, TileIndex tile) override; + bool HandleMousePress() override; + void HandleMouseRelease() override; + std::vector> GetOverlayData(); +}; + + +class VanillaStationPreview : public StationPreviewBase { +protected: + SpriteID palette; + + void Execute() override; + +public: + StationID selected_station_to_join = INVALID_STATION; + + VanillaStationPreview(sp type) :StationPreviewBase{type} {}; + virtual ~VanillaStationPreview() {}; + void Update(Point pt, TileIndex tile) override; + + Preview::TileMap GetTiles() override; + void OnStationRemoved(const Station *station) override; +}; + + +class StationPreview : public StationPreviewBase { +protected: + bool select_mode = false; + + void Execute() override; + up GetCommand(); + +public: + StationPreview(sp type); + virtual ~StationPreview(); + void Update(Point pt, TileIndex tile) override; + bool HandleMousePress() override; + + Preview::TileMap GetTiles() override; + void OnStationRemoved(const Station *station) override; +}; + +// SPR_CURSOR_BUS_STATION SPR_CURSOR_TRUCK_STATION + +bool HandleStationPlacePushButton(Window *w, WidgetID widget, sp type); + } // namespace citymania diff --git a/src/citymania/cm_type.hpp b/src/citymania/cm_type.hpp index 0c636af7da..85a4ecae16 100644 --- a/src/citymania/cm_type.hpp +++ b/src/citymania/cm_type.hpp @@ -1,6 +1,9 @@ #ifndef CMEXT_TYPE_HPP #define CMEXT_TYPE_HPP +#include "../core/geometry_type.hpp" +#include "../core/overflowsafe_type.hpp" + #include #include #include @@ -71,4 +74,26 @@ enum class ControllerType: uint8_t { } // namespace citymania + +template +struct fmt::formatter> { + constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + + template + auto format(const OverflowSafeInt& i, FormatContext& ctx) { + return fmt::format_to(ctx.out(), "{}", static_cast(i)); + } +}; + +template <> +struct fmt::formatter { + constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + + template + auto format(const Point& pt, FormatContext& ctx) { + return fmt::format_to(ctx.out(), "({},{})", pt.x, pt.y); + } +}; + + #endif diff --git a/src/main_gui.cpp b/src/main_gui.cpp index 1fb554b1ed..24d948a645 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp @@ -7,9 +7,12 @@ /** @file main_gui.cpp Handling of the main viewport. */ +#include "3rdparty/fmt/core.h" +#include "citymania/cm_highlight.hpp" #include "stdafx.h" #include "currency.h" #include "spritecache.h" +#include "viewport_type.h" #include "window_gui.h" #include "window_func.h" #include "textbuf_gui.h" @@ -50,6 +53,8 @@ #include "debug.h" #include "citymania/cm_commands.hpp" +#include "citymania/cm_highlight.hpp" +#include "citymania/cm_station_gui.hpp" #include "safeguards.h" @@ -76,6 +81,13 @@ 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()); + // } else { + // citymania::SetActivePreview(std::make_unique()); + // } + // } w->LowerWidget(widget); return true; } diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 240b31a5f2..0f291deee0 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -7,6 +7,7 @@ /** @file rail_gui.cpp %File for dealing with rail construction user interface */ +#include "citymania/cm_highlight_type.hpp" #include "stdafx.h" #include "gui.h" #include "window_gui.h" @@ -58,10 +59,11 @@ /* CityMania code end */ #include "safeguards.h" +#include RailType _cur_railtype; ///< Rail type of the current build-rail toolbar. -static bool _remove_button_clicked; ///< Flag whether 'remove' toggle-button is currently enabled +extern bool _remove_button_clicked; ///< Flag whether 'remove' toggle-button is currently enabled static bool _cm_invert_remove; ///< Invert remove mode on tools (when fn-clicked) /* CM static */ DiagDirection _build_depot_direction; ///< Currently selected depot direction static uint16_t _cur_waypoint_type; ///< Currently selected waypoint type @@ -79,15 +81,16 @@ static const int HOTKEY_BLUEPRINT_LOAD_END = 0x1030; static const int HOTKEY_BLUEPRINT_SAVE = 0x1030; static const int HOTKEY_BLUEPRINT_SAVE_END = 0x1040; -struct RailStationGUISettings { - Axis orientation; ///< Currently selected rail station orientation +// Moved to cm_station_gui.hpp +// struct RailStationGUISettings { +// Axis orientation; ///< Currently selected rail station orientation - bool newstations; ///< Are custom station definitions available? - StationClassID station_class; ///< Currently selected custom station class (if newstations is \c true ) - uint16_t station_type; ///< %Station type within the currently selected custom station class (if newstations is \c true ) - uint16_t station_count; ///< Number of custom stations (if newstations is \c true ) -}; -RailStationGUISettings _railstation; ///< Settings of the station builder GUI +// bool newstations; ///< Are custom station definitions available? +// StationClassID station_class; ///< Currently selected custom station class (if newstations is \c true ) +// uint16_t station_type; ///< %Station type within the currently selected custom station class (if newstations is \c true ) +// uint16_t station_count; ///< Number of custom stations (if newstations is \c true ) +// }; +citymania::RailStationGUISettings _railstation; ///< Settings of the station builder GUI static void HandleStationPlacement(TileIndex start, TileIndex end); @@ -214,7 +217,7 @@ static void PlaceRail_Station(TileIndex tile) int h = _settings_client.gui.station_platlength; if (!_railstation.orientation) Swap(w, h); - RailStationGUISettings params = _railstation; + citymania::RailStationGUISettings params = _railstation; RailType rt = _cur_railtype; byte numtracks = _settings_client.gui.station_numtracks; byte platlength = _settings_client.gui.station_platlength; @@ -677,12 +680,12 @@ struct BuildRailToolbarWindow : Window { if (was_open) ResetObjectToPlace(); if (!was_open || dragdrop != _settings_client.gui.station_dragdrop) { _settings_client.gui.station_dragdrop = dragdrop; - if (HandlePlacePushButton(this, WID_RAT_BUILD_STATION, SPR_CURSOR_RAIL_STATION, HT_RECT, DDSP_BUILD_STATION)) + if (citymania::HandleStationPlacePushButton(this, WID_RAT_BUILD_STATION, std::make_shared())) ShowStationBuilder(this); } this->last_user_action = WID_RAT_BUILD_STATION; } else { /* button */ - if (HandlePlacePushButton(this, WID_RAT_BUILD_STATION, SPR_CURSOR_RAIL_STATION, HT_RECT, DDSP_BUILD_STATION)) { + if (citymania::HandleStationPlacePushButton(this, WID_RAT_BUILD_STATION, std::make_shared())) { ShowStationBuilder(this); this->last_user_action = WID_RAT_BUILD_STATION; } @@ -1131,7 +1134,7 @@ static void HandleStationPlacement(TileIndex start, TileIndex end) if (_railstation.orientation == AXIS_X) Swap(numtracks, platlength); - RailStationGUISettings params = _railstation; + citymania::RailStationGUISettings params = _railstation; RailType rt = _cur_railtype; bool adjacent = citymania::_fn_mod; diff --git a/src/road_gui.cpp b/src/road_gui.cpp index a880b5b726..df6c0c65ca 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -56,7 +56,7 @@ static void ShowRVStationPicker(Window *parent, RoadStopType rs); static void ShowRoadDepotPicker(Window *parent); -static bool _remove_button_clicked; +extern bool _remove_button_clicked; static bool _one_way_button_clicked; static Axis _place_road_dir; @@ -517,15 +517,16 @@ struct BuildRoadToolbarWindow : Window { } break; - case WID_ROT_BUS_STATION: - if (HandlePlacePushButton(this, WID_ROT_BUS_STATION, SPR_CURSOR_BUS_STATION, HT_RECT, DDSP_BUILD_BUSSTOP)) { + case WID_ROT_BUS_STATION: { + if (citymania::HandleStationPlacePushButton(this, WID_ROT_BUS_STATION, std::make_shared(ROADSTOP_BUS))) { ShowRVStationPicker(this, ROADSTOP_BUS); this->last_started_action = widget; } break; + } case WID_ROT_TRUCK_STATION: - if (HandlePlacePushButton(this, WID_ROT_TRUCK_STATION, SPR_CURSOR_TRUCK_STATION, HT_RECT, DDSP_BUILD_TRUCKSTOP)) { + if (citymania::HandleStationPlacePushButton(this, WID_ROT_BUS_STATION, std::make_shared(ROADSTOP_TRUCK))) { ShowRVStationPicker(this, ROADSTOP_TRUCK); this->last_started_action = widget; } diff --git a/src/station_gui.cpp b/src/station_gui.cpp index df74f5fab9..a30caa71f4 100644 --- a/src/station_gui.cpp +++ b/src/station_gui.cpp @@ -7,6 +7,7 @@ /** @file station_gui.cpp The GUI for stations. */ +#include "station_type.h" #include "stdafx.h" #include "debug.h" #include "gui.h" @@ -42,6 +43,7 @@ #include "citymania/cm_tooltips.hpp" #include "safeguards.h" +#include /** * Calculates and draws the accepted or supplied cargo around the selected tile(s) @@ -2347,7 +2349,8 @@ struct SelectStationWindow : WindowPopup { void Close([[maybe_unused]] int data = 0) override { - SetViewportCatchmentSpecializedStation(nullptr, true); + if constexpr (std::is_same_v) SetViewportCatchmentSpecializedStation(nullptr, true); + else citymania::SetSelectedStationToJoin(INVALID_STATION); _thd.freeze = false; this->Window::Close(); @@ -2435,14 +2438,16 @@ struct SelectStationWindow : WindowPopup { void OnMouseOver([[maybe_unused]] Point pt, WidgetID widget) override { if (widget != WID_JS_PANEL) { - SetViewportCatchmentSpecializedStation(nullptr, true); + if constexpr (std::is_same_v) SetViewportCatchmentSpecializedStation(nullptr, true); + else citymania::SetSelectedStationToJoin(INVALID_STATION); return; } /* Show coverage area of station under cursor */ 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); - SetViewportCatchmentSpecializedStation(st, true); + if constexpr (std::is_same_v) SetViewportCatchmentSpecializedStation(st, true); + else citymania::SetSelectedStationToJoin(*it); } }; @@ -2478,7 +2483,6 @@ static bool StationJoinerNeeded(TileArea ta, const StationPickerCmdProc &proc) /* only show the popup, if we press ctrl */ if (!citymania::_fn_mod) return false; - /* Now check if we could build there */ if (!proc(true, INVALID_STATION)) return false; diff --git a/src/tilehighlight_type.h b/src/tilehighlight_type.h index adcfdbd279..249ff6344c 100644 --- a/src/tilehighlight_type.h +++ b/src/tilehighlight_type.h @@ -10,6 +10,7 @@ #ifndef TILEHIGHLIGHT_TYPE_H #define TILEHIGHLIGHT_TYPE_H +#include "citymania/cm_command_type.hpp" #include "citymania/cm_highlight_type.hpp" #include "core/geometry_type.hpp" @@ -89,6 +90,7 @@ struct TileHighlightData { ViewportPlaceMethod select_method; ///< The method which governs how tiles are selected. ViewportDragDropSelectionProcess select_proc; ///< The procedure that has to be called when the selection is done. + // Old CM highilight citymania::ObjectHighlight cm; citymania::ObjectHighlight cm_new; bool cm_poly_terra; diff --git a/src/viewport.cpp b/src/viewport.cpp index dff44c7fbc..d356165e6b 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -2200,7 +2200,7 @@ void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_heigh * * @ingroup dirty */ -static void SetSelectionTilesDirty() +/* CM static */ void SetSelectionTilesDirty() { int x_size = _thd.size.x; int y_size = _thd.size.y; @@ -2915,6 +2915,8 @@ void UpdateTileSelection() /* Draw the new tile selection? */ if ((new_drawstyle & HT_DRAG_MASK) != HT_NONE) SetSelectionTilesDirty(); } + + citymania::UpdateActivePreview(); } /** @@ -3993,6 +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) { + citymania::ResetActivePreview(); if (_thd.window_class != WC_INVALID) { /* Undo clicking on button and drag & drop */ Window *w = _thd.GetCallbackWnd(); @@ -4043,6 +4046,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(); } Point GetViewportStationMiddle(const Viewport *vp, const Station *st) diff --git a/src/window.cpp b/src/window.cpp index 9576736f18..ba2143acfe 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -42,6 +42,7 @@ /* CityMania code start */ #include #include "window_gui.h" +#include "citymania/cm_highlight.hpp" #include "citymania/cm_hotkeys.hpp" #include "citymania/cm_overlays.hpp" #include "citymania/cm_tooltips.hpp" @@ -2811,6 +2812,7 @@ static void MouseLoop(MouseClick click, int mousewheel) if (HandleActiveWidget() == ES_HANDLED) return; if (HandleViewportScroll() == ES_HANDLED) return; + if (citymania::HandleMouseMove()) return; HandleMouseOver(); bool scrollwheel_scrolling = _settings_client.gui.scrollwheel_scrolling == 1 && (_cursor.v_wheel != 0 || _cursor.h_wheel != 0); @@ -2845,6 +2847,7 @@ static void MouseLoop(MouseClick click, int mousewheel) switch (click) { case MC_DOUBLE_LEFT: case MC_LEFT: + if (citymania::HandleMouseClick(vp, click == MC_DOUBLE_LEFT)) return; if (HandleViewportClicked(vp, x, y, click == MC_DOUBLE_LEFT)) return; if (!(w->flags & WF_DISABLE_VP_SCROLL) && _settings_client.gui.scroll_mode == VSM_MAP_LMB) {