diff --git a/source.list b/source.list index d03aa9cfad..cf75873586 100644 --- a/source.list +++ b/source.list @@ -1230,6 +1230,7 @@ citymania/cm_base64.hpp citymania/cm_base64.cpp citymania/cm_highlight.hpp citymania/cm_highlight.cpp +citymania/cm_highlight_type.hpp citymania/cm_locations.hpp citymania/cm_locations.cpp citymania/cm_minimap.hpp diff --git a/src/citymania/cm_highlight.cpp b/src/citymania/cm_highlight.cpp index 741517f8ac..c95e299fce 100644 --- a/src/citymania/cm_highlight.cpp +++ b/src/citymania/cm_highlight.cpp @@ -9,12 +9,14 @@ #include "../house.h" #include "../industry.h" #include "../landscape.h" +#include "../newgrf_railtype.h" #include "../town.h" #include "../town_kdtree.h" #include "../tilearea_type.h" #include "../tilehighlight_type.h" #include "../viewport_func.h" #include "../zoning.h" +#include "../table/track_land.h" #include @@ -30,9 +32,16 @@ extern void DrawSelectionSprite(SpriteID image, PaletteID pal, const TileInfo *t extern const Station *_viewport_highlight_station; extern TileHighlightData _thd; extern bool IsInsideSelectedRectangle(int x, int y); +extern RailType _cur_railtype; +RoadBits FindRailsToConnect(TileIndex tile); +extern DiagDirection _build_depot_direction; ///< Currently selected depot direction + namespace citymania { +extern void (*DrawTileSelectionRect)(const TileInfo *ti, PaletteID pal); +extern void (*DrawAutorailSelection)(const TileInfo *ti, HighLightStyle autorail_type, PaletteID pal); + struct TileZoning { uint8 town_zone : 3; uint8 industry_fund_result : 2; @@ -61,6 +70,149 @@ const byte _tileh_to_sprite[32] = { }; +ObjectTileHighlight ObjectTileHighlight::make_depot(DiagDirection ddir) { + auto oh = ObjectTileHighlight(Type::RAIL_DEPOT); + oh.u.depot.ddir = ddir; + return oh; +} + +ObjectTileHighlight ObjectTileHighlight::make_rail(Track track) { + auto oh = ObjectTileHighlight(Type::RAIL_TRACK); + oh.u.rail.track = track; + return oh; +} + + +bool ObjectHighlight::operator==(const ObjectHighlight& oh) { + if (this->type != oh.type) return false; + switch (this->type) { + case Type::RAIL_DEPOT: return this->u.depot.tile == oh.u.depot.tile && this->u.depot.ddir == oh.u.depot.ddir; + default: return true; + } + return true; +} + +bool ObjectHighlight::operator!=(const ObjectHighlight& oh) { + return !(*this == oh); +} + + +ObjectHighlight ObjectHighlight::make_depot(TileIndex tile, DiagDirection ddir) { + auto oh = ObjectHighlight(ObjectHighlight::Type::RAIL_DEPOT); + oh.u.depot.tile = tile; + oh.u.depot.ddir = ddir; + 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 + * @param dir Direction to check for already present tracks + * @param track Track to add + * @see CcRailDepot() + */ +void ObjectHighlight::PlaceExtraDepotRail(TileIndex tile, DiagDirection dir, Track track) +{ + if (GetRailTileType(tile) != RAIL_TILE_NORMAL) return; + if ((GetTrackBits(tile) & DiagdirReachesTracks(dir)) == 0) return; + + this->tiles.insert(std::make_pair(tile, ObjectTileHighlight::make_rail(track))); +} + +/** Additional pieces of track to add at the entrance of a depot. */ +static const Track _place_depot_extra_track[12] = { + TRACK_LEFT, TRACK_UPPER, TRACK_UPPER, TRACK_RIGHT, // First additional track for directions 0..3 + TRACK_X, TRACK_Y, TRACK_X, TRACK_Y, // Second additional track + TRACK_LOWER, TRACK_LEFT, TRACK_RIGHT, TRACK_LOWER, // Third additional track +}; + +/** Direction to check for existing track pieces. */ +static const DiagDirection _place_depot_extra_dir[12] = { + DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_SE, DIAGDIR_SW, + DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NE, DIAGDIR_SE, + DIAGDIR_NW, DIAGDIR_NE, DIAGDIR_NW, DIAGDIR_NE, +}; + +void ObjectHighlight::UpdateTiles() { + this->tiles.clear(); + switch (this->type) { + case Type::RAIL_DEPOT: { + auto dir = this->u.depot.ddir; + this->tiles.insert(std::make_pair(this->u.depot.tile, ObjectTileHighlight::make_depot(dir))); + auto tile = this->u.depot.tile + TileOffsByDiagDir(dir); + if (IsTileType(tile, MP_RAILWAY) && IsCompatibleRail(GetRailType(tile), _cur_railtype)) { + PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir], _place_depot_extra_track[dir]); + PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir + 4], _place_depot_extra_track[dir + 4]); + PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir + 8], _place_depot_extra_track[dir + 8]); + } + break; + } + default: + break; + } +} + +void ObjectHighlight::MarkDirty() { + this->UpdateTiles(); + for (const auto &kv: this->tiles) { + MarkTileDirtyByTile(kv.first); + } +} + + +void DrawTrainDepotSprite(const TileInfo *ti, RailType railtype, DiagDirection ddir) +{ + const DrawTileSprites *dts = &_depot_gfx_table[ddir]; + const RailtypeInfo *rti = GetRailTypeInfo(railtype); + SpriteID image = rti->UsesOverlay() ? SPR_FLAT_GRASS_TILE : dts->ground.sprite; + uint32 offset = rti->GetRailtypeSpriteOffset(); + + if (image != SPR_FLAT_GRASS_TILE) image += offset; + PaletteID palette = COMPANY_SPRITE_COLOUR(_local_company); + + // DrawSprite(image, PAL_NONE, x, y); + + switch (ddir) { + case DIAGDIR_SW: DrawAutorailSelection(ti, HT_DIR_X, PAL_NONE); break; + case DIAGDIR_SE: DrawAutorailSelection(ti, HT_DIR_Y, PAL_NONE); break; + default: break; + } + // if (rti->UsesOverlay()) { + // SpriteID ground = GetCustomRailSprite(rti, INVALID_TILE, RTSG_GROUND); + + // switch (ddir) { + // case DIAGDIR_SW: DrawSprite(ground + RTO_X, PALETTE_TINT_WHITE, x, y); break; + // case DIAGDIR_SE: DrawSprite(ground + RTO_Y, PALETTE_TINT_WHITE, x, y); break; + // default: break; + // } + // } + int depot_sprite = GetCustomRailSprite(rti, INVALID_TILE, RTSG_DEPOT); + if (depot_sprite != 0) offset = depot_sprite - SPR_RAIL_DEPOT_SE_1; + + DrawRailTileSeq(ti, dts, TO_INVALID, offset, 0, PALETTE_TINT_WHITE); +} + +void ObjectHighlight::Draw(const TileInfo *ti) { + this->UpdateTiles(); + 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(ti, _cur_railtype, oth.u.depot.ddir); + break; + case ObjectTileHighlight::Type::RAIL_TRACK: { + auto hs = (HighLightStyle)oth.u.rail.track; + DrawAutorailSelection(ti, hs, PAL_NONE); + break; + } + default: + break; + } + } +} + + template uint8 Get(uint32 x, uint32 y, F getter) { if (x >= MapSizeX() || y >= MapSizeY()) return 0; @@ -372,7 +524,7 @@ TileHighlight GetTileHighlight(const TileInfo *ti) { return th; } -void DrawTileSelection(const TileInfo *ti, const TileHighlight &th) { +void DrawTileZoning(const TileInfo *ti, const TileHighlight &th) { for (uint i = 0; i < th.border_count; i++) DrawBorderSprites(ti, th.border[i], th.border_color[i]); if (th.sprite) { @@ -385,6 +537,94 @@ void DrawTileSelection(const TileInfo *ti, const TileHighlight &th) { } } +bool DrawTileSelection(const TileInfo *ti, const TileHighlightType &tht) { + _thd.cm.Draw(ti); + + if ((_thd.drawstyle & HT_DRAG_MASK) == HT_RECT && _thd.outersize.x > 0) { + // station selector, handled by DrawTileZoning + return true; + } + + if ((_thd.drawstyle & HT_DRAG_MASK) == HT_RECT && IsInsideSelectedRectangle(ti->x, ti->y) + && _cursor.sprite_seq[0].sprite == GetRailTypeInfo(_cur_railtype)->cursor.depot) { + // DrawTileSelectionRect(ti, _thd.make_square_red ? PALETTE_SEL_TILE_RED : PAL_NONE); + + // auto rti = GetRailTypeInfo(_cur_railtype); + // int depot_sprite = GetCustomRailSprite(rti, ti->tile, RTSG_DEPOT); + // auto relocation = depot_sprite != 0 ? depot_sprite - SPR_RAIL_DEPOT_SE_1 : rti->GetRailtypeSpriteOffset(); + // AddSortableSpriteToDraw(relocation, PALETTE_TINT_WHITE, ti->x, ti->y, 0x10, 0x10, 1, ti->z); + // AddSortableSpriteToDraw(SPR_RAIL_DEPOT_SE_1, PALETTE_TINT_WHITE, ti->x, ti->y, 0x10, 0x10, 1, ti->z); + // DrawTrainDepotSprite(r.left + 1 + ScaleGUITrad(31), r.bottom - ScaleGUITrad(31), widget - WID_BRAD_DEPOT_NE + DIAGDIR_NE, _cur_railtype); + // DrawTrainDepotSprite(ti, _cur_railtype, (DiagDirection)(_thd.drawstyle & HT_DIR_MASK)); + return true; + } + + return false; +} + + +// almost duplicate from road_gui.cpp +static DiagDirection TileFractCoordsToDiagDir(Point pt) { + auto x = pt.x & TILE_UNIT_MASK; + auto y = pt.y & TILE_UNIT_MASK; + bool diag = (x + y) < 16; + if (x < y) { + return diag ? DIAGDIR_NE : DIAGDIR_SE; + } + return diag ? DIAGDIR_NW : DIAGDIR_SW; +} + +// FIXME duplicate from road_gui.cpp +static DiagDirection RoadBitsToDiagDir(RoadBits bits) { + if (bits < ROAD_SE) { + return bits == ROAD_NW ? DIAGDIR_NW : DIAGDIR_SW; + } + return bits == ROAD_SE ? DIAGDIR_SE : DIAGDIR_NE; +} + +DiagDirection AutodetectRailObjectDirection(TileIndex tile, Point pt) { + RoadBits bits = FindRailsToConnect(tile); + // FIXME after this point repeats road autodetection + if (HasExactlyOneBit(bits)) return RoadBitsToDiagDir(bits); + if (bits == ROAD_NONE) bits = ROAD_ALL; + RoadBits frac_bits = DiagDirToRoadBits(TileFractCoordsToDiagDir(pt)); + if (HasExactlyOneBit(frac_bits & bits)) { + return RoadBitsToDiagDir(frac_bits & bits); + } + frac_bits |= MirrorRoadBits(frac_bits); + if (HasExactlyOneBit(frac_bits & bits)) { + return RoadBitsToDiagDir(frac_bits & bits); + } + for (DiagDirection ddir = DIAGDIR_BEGIN; ddir < DIAGDIR_END; ddir++) { + if (DiagDirToRoadBits(ddir) & bits) { + return ddir; + } + } + NOT_REACHED(); +} + +HighLightStyle UpdateTileSelection(HighLightStyle new_drawstyle) { + _thd.cm_new = ObjectHighlight(ObjectHighlight::Type::NONE); + if ((_thd.place_mode & HT_DRAG_MASK) == HT_RECT && + _cursor.sprite_seq[0].sprite == GetRailTypeInfo(_cur_railtype)->cursor.depot) { + auto dir = _build_depot_direction; + auto pt = GetTileBelowCursor(); + auto tile = TileVirtXY(pt.x, pt.y); + if (pt.x != -1) { + if (dir >= DiagDirection::DIAGDIR_END) + dir = AutodetectRailObjectDirection(tile, pt); + _thd.cm_new = ObjectHighlight::make_depot(tile, dir); + } + new_drawstyle = HT_RECT; + } + if (_thd.cm != _thd.cm_new) { + _thd.cm.MarkDirty(); + _thd.cm = _thd.cm_new; + _thd.cm.MarkDirty(); + } + return new_drawstyle; +} + void AllocateZoningMap(uint map_size) { free(_mz); _mz = CallocT(map_size); diff --git a/src/citymania/cm_highlight.hpp b/src/citymania/cm_highlight.hpp index 60cf1a736e..f3a5495f15 100644 --- a/src/citymania/cm_highlight.hpp +++ b/src/citymania/cm_highlight.hpp @@ -1,15 +1,25 @@ #ifndef CITYMANIA_HIGHLIGHT_HPP #define CITYMANIA_HIGHLIGHT_HPP +#include "cm_highlight_type.hpp" + #include "../core/enum_type.hpp" #include "../gfx_type.h" #include "../industry_type.h" #include "../tile_cmd.h" #include "../tile_type.h" +#include "../tilehighlight_type.h" #include "../town_type.h" #include "../table/sprites.h" +enum TileHighlightType { + THT_NONE, + THT_WHITE, + THT_BLUE, + THT_RED, +}; + namespace citymania { ////enum class ZoningBorder : unt8 { @@ -64,13 +74,15 @@ DECLARE_ENUM_AS_BIT_SET(ZoningBorder); TileHighlight GetTileHighlight(const TileInfo *ti); -void DrawTileSelection(const TileInfo *ti, const TileHighlight &th); +void DrawTileZoning(const TileInfo *ti, const TileHighlight &th); +bool DrawTileSelection(const TileInfo *ti, const TileHighlightType &tht); void AllocateZoningMap(uint map_size); void InitializeZoningMap(); void UpdateTownZoning(Town *town, uint32 prev_edge); void UpdateZoningTownHouses(const Town *town, uint32 old_houses); +HighLightStyle UpdateTileSelection(HighLightStyle new_drawstyle); std::pair GetTownZoneBorder(TileIndex tile); ZoningBorder GetAnyStationCatchmentBorder(TileIndex tlie); diff --git a/src/citymania/cm_highlight_type.hpp b/src/citymania/cm_highlight_type.hpp new file mode 100644 index 0000000000..81bbfb75ba --- /dev/null +++ b/src/citymania/cm_highlight_type.hpp @@ -0,0 +1,71 @@ +#ifndef CITYMANIA_HIGHLIGHT_TYPE_HPP +#define CITYMANIA_HIGHLIGHT_TYPE_HPP + +#include "../direction_type.h" +#include "../tile_cmd.h" +#include "../tile_type.h" +#include "../track_type.h" + +#include + +namespace citymania { + + +class ObjectTileHighlight { +public: + enum class Type { + RAIL_DEPOT = 0, + RAIL_TRACK = 1, + }; + + Type type; + union { + struct { + DiagDirection ddir; + } depot; + struct { + Track track; + } rail; + } u; + + ObjectTileHighlight(Type type): type{type} {} + static ObjectTileHighlight make_depot(DiagDirection ddir); + static ObjectTileHighlight make_rail(Track track); +}; + +class ObjectHighlight { +public: + enum class Type { + NONE = 0, + RAIL_DEPOT = 1, + }; + +protected: + Type type; + union { + struct { + TileIndex tile; + DiagDirection ddir; + } depot; + } u; + + bool tiles_updated = false; + std::multimap tiles; + void UpdateTiles(); + void PlaceExtraDepotRail(TileIndex tile, DiagDirection dir, Track track); + +public: + ObjectHighlight(Type type = Type::NONE): type{type} { /* get rid of uninitualized warning */ this->u.depot.tile = INVALID_TILE; } + bool operator==(const ObjectHighlight& oh); + bool operator!=(const ObjectHighlight& oh); + + static ObjectHighlight make_depot(TileIndex tile, DiagDirection ddir); + + void Draw(const TileInfo *ti); + void MarkDirty(); +}; + + +} // namespace citymania + +#endif diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index dfea49b17a..280df4d2f4 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -47,7 +47,7 @@ RailType _cur_railtype; ///< Rail type of the current build-rail toolbar. static 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) -static DiagDirection _build_depot_direction; ///< Currently selected depot direction +/* CM static */ DiagDirection _build_depot_direction; ///< Currently selected depot direction static byte _waypoint_count = 1; ///< Number of waypoint types static byte _cur_waypoint_type; ///< Currently selected waypoint type static bool _convert_signal_button; ///< convert signal button in the signal GUI pressed @@ -506,7 +506,7 @@ RoadBits FindRailsToConnect(TileIndex tile) { /* * Selects orientation for rail object (depot) */ -static DiagDirection AutodetectRailObjectDirection(TileIndex tile) { +DiagDirection AutodetectRailObjectDirection(TileIndex tile) { RoadBits bits = FindRailsToConnect(tile); // FIXME after this point repeats road autodetection if (HasExactlyOneBit(bits)) return RoadBitsToDiagDir(bits); @@ -713,7 +713,7 @@ struct BuildRailToolbarWindow : Window { break; case WID_RAT_BUILD_DEPOT: - if (HandlePlacePushButton(this, WID_RAT_BUILD_DEPOT, GetRailTypeInfo(_cur_railtype)->cursor.depot, HT_RECT)) { + if (HandlePlacePushButton(this, WID_RAT_BUILD_DEPOT, GetRailTypeInfo(_cur_railtype)->cursor.depot, HT_RECT | (HighLightStyle)_build_depot_direction)) { ShowBuildTrainDepotPicker(this); this->last_user_action = widget; } diff --git a/src/tilehighlight_type.h b/src/tilehighlight_type.h index 171252c6fa..d1b149b786 100644 --- a/src/tilehighlight_type.h +++ b/src/tilehighlight_type.h @@ -10,6 +10,8 @@ #ifndef TILEHIGHLIGHT_TYPE_H #define TILEHIGHLIGHT_TYPE_H +#include "citymania/cm_highlight_type.hpp" + #include "core/geometry_type.hpp" #include "window_type.h" #include "tile_type.h" @@ -39,6 +41,7 @@ enum HighLightStyle { HT_DIR_VR = 5, ///< vertical right HT_DIR_END, ///< end marker HT_DIR_MASK = 0x7, ///< masks the drag-direction + CM_HT_DIR_AUTO = 4, }; DECLARE_ENUM_AS_BIT_SET(HighLightStyle) @@ -85,6 +88,9 @@ 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. + citymania::ObjectHighlight cm; + citymania::ObjectHighlight cm_new; + void Reset(); bool IsDraggingDiagonal(); diff --git a/src/viewport.cpp b/src/viewport.cpp index 9b8aa37c44..e6a5cc6623 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -1018,12 +1018,13 @@ static void DrawAutorailSelection(const TileInfo *ti, HighLightStyle autorail_ty DrawSelectionSprite(image, pal, ti, 7, foundation_part); } -enum TileHighlightType { - THT_NONE, - THT_WHITE, - THT_BLUE, - THT_RED, -}; +// CM moved to cm_highlight.hpp +// enum TileHighlightType { +// THT_NONE, +// THT_WHITE, +// THT_BLUE, +// THT_RED, +// }; const Station *_viewport_highlight_station; ///< Currently selected station for coverage area highlight const Town *_viewport_highlight_town; ///< Currently selected town for coverage area highlight @@ -1128,10 +1129,7 @@ static void DrawTileSelection(const TileInfo *ti) TileHighlightType tht = GetTileHighlightType(ti->tile); DrawTileHighlightType(ti, tht); - if ((_thd.drawstyle & HT_DRAG_MASK) == HT_RECT && _thd.outersize.x > 0) { - // station selector, handled by citymania highlight - return; - } + if (citymania::DrawTileSelection(ti, tht)) return; switch (_thd.drawstyle & HT_DRAG_MASK) { default: break; // No tile selection active? @@ -1318,7 +1316,7 @@ static void ViewportAddLandscape() if (tile_info.tile != INVALID_TILE){ DrawTileZoning(&tile_info); - citymania::DrawTileSelection(&tile_info, _vd.cm_highlight); + citymania::DrawTileZoning(&tile_info, _vd.cm_highlight); DrawTileSelection(&tile_info); } } @@ -2752,6 +2750,7 @@ void UpdateTileSelection() } if (new_drawstyle & HT_LINE) CalcNewPolylineOutersize(); + new_drawstyle = citymania::UpdateTileSelection(new_drawstyle); /* redraw selection */ if (_thd.drawstyle != new_drawstyle || @@ -4165,3 +4164,8 @@ void ResetRailPlacementEndpoints() _rail_snap_points.clear(); _current_snap_lock.x = -1; } + +namespace citymania { + void (*DrawTileSelectionRect)(const TileInfo *ti, PaletteID pal) = &::DrawTileSelectionRect; + void (*DrawAutorailSelection)(const TileInfo *ti, HighLightStyle autorail_type, PaletteID pal) = &::DrawAutorailSelection; +}