diff --git a/source.list b/source.list index 6c17fa94db..007256ca7f 100644 --- a/source.list +++ b/source.list @@ -1228,6 +1228,8 @@ citymania/extensions/cmext_company.hpp # CityMania client citymania/cm_base64.hpp citymania/cm_base64.cpp +citymania/cm_blueprint.hpp +citymania/cm_blueprint.cpp citymania/cm_highlight.hpp citymania/cm_highlight.cpp citymania/cm_highlight_type.hpp diff --git a/src/citymania/cm_blueprint.cpp b/src/citymania/cm_blueprint.cpp new file mode 100644 index 0000000000..fb312abc79 --- /dev/null +++ b/src/citymania/cm_blueprint.cpp @@ -0,0 +1,563 @@ +#include "../stdafx.h" + +#include "cm_blueprint.hpp" + +#include "cm_highlight.hpp" + +#include "../command_func.h" +#include "../direction_type.h" +#include "../rail_map.h" +#include "../station_map.h" +#include "../station_base.h" +#include "../tilearea_type.h" +#include "../tunnelbridge_map.h" +#include "../network/network.h" + +#include +#include + +extern TileHighlightData _thd; +extern RailType _cur_railtype; + +namespace citymania { + + +std::pair> _active_blueprint = std::make_pair(INVALID_TILE, nullptr); + +TileIndexDiffC operator+(const TileIndexDiffC &a, const TileIndexDiffC &b) { + return TileIndexDiffC{(int16)(a.x + b.x), (int16)(a.y + b.y)}; +} + +bool operator==(const TileIndexDiffC &a, const TileIndexDiffC &b) { + return a.x == b.x && a.y == b.y; +} + +bool operator!=(const TileIndexDiffC &a, const TileIndexDiffC &b) { + return a.x != b.x || a.y != b.y; +} + +typedef std::tuple CommandTuple; +typedef std::function CommandCallback; +std::map> _command_callbacks; + +void AddCommandCallback(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback callback) { + _command_callbacks[std::make_tuple(tile, p1, p2, cmd)].push_back(callback); +} + +void DoCommandWithCallback(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback callback) { + AddCommandCallback(tile, p1, p2, cmd, callback); + DoCommandP(tile, p1, p2, cmd); +} + +void CommandExecuted(bool res, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd) { + CommandTuple ct {tile, p1, p2, cmd}; + auto p = _command_callbacks.find(ct); + if (p == _command_callbacks.end()) return; + for (auto &cb : p->second) + cb(res); + _command_callbacks.erase(p); +} + +void Blueprint::Add(Blueprint::Item item) { + this->items.push_back(item); + switch (item.type) { + case Item::Type::RAIL_TRACK: { + auto tdir = item.u.rail.track.start_dir; + auto tdiff = item.tdiff; + // fprintf(stderr, "TRACK %u %u\n", (uint)tdiff, (uint)item.u.rail.track.end_diff); + for (auto i = 0; i < item.u.rail.track.length; i++) { + this->tiles.insert(tdiff); + tdiff = tdiff + TileIndexDiffCByDiagDir(TrackdirToExitdir(tdir)); + tdir = NextTrackdir(tdir); + } + break; + } + case Item::Type::RAIL_BRIDGE: + case Item::Type::RAIL_TUNNEL: + this->tiles.insert(item.u.rail.tunnel.other_end); + FALLTHROUGH; + case Item::Type::RAIL_DEPOT: + case Item::Type::RAIL_STATION: + case Item::Type::RAIL_STATION_PART: + this->tiles.insert(item.tdiff); + break; + case Item::Type::RAIL_SIGNAL: // tile is added for track anyway + break; + default: + NOT_REACHED(); + } +} + +std::multimap Blueprint::GetTiles(TileIndex tile) { + std::multimap res; + if (tile == INVALID_TILE) return res; + auto add_tile = [&res](TileIndex tile, const ObjectTileHighlight &ohl) { + if (tile == INVALID_TILE) return; + res.emplace(tile, ohl); + }; + for (auto &o: this->items) { + auto otile = AddTileIndexDiffCWrap(tile, o.tdiff); + switch(o.type) { + case Item::Type::RAIL_TRACK: { + auto end_tile = otile; + auto tdir = o.u.rail.track.start_dir; + for (auto i = 0; i < o.u.rail.track.length; i++) { + add_tile(end_tile, ObjectTileHighlight::make_rail_track(TrackdirToTrack(tdir))); + end_tile = TileAddByDiagDir(end_tile, TrackdirToExitdir(tdir)); + tdir = NextTrackdir(tdir); + } + break; + } + case Item::Type::RAIL_BRIDGE: + add_tile(otile, ObjectTileHighlight::make_rail_bridge_head(o.u.rail.bridge.ddir, o.u.rail.bridge.type)); + add_tile(AddTileIndexDiffCWrap(tile, o.u.rail.bridge.other_end), ObjectTileHighlight::make_rail_bridge_head(ReverseDiagDir(o.u.rail.bridge.ddir), o.u.rail.bridge.type)); + break; + case Item::Type::RAIL_TUNNEL: + add_tile(otile, ObjectTileHighlight::make_rail_tunnel_head(o.u.rail.tunnel.ddir)); + add_tile(AddTileIndexDiffCWrap(tile, o.u.rail.tunnel.other_end), ObjectTileHighlight::make_rail_tunnel_head(ReverseDiagDir(o.u.rail.tunnel.ddir))); + break; + case Item::Type::RAIL_DEPOT: + add_tile(otile, ObjectTileHighlight::make_rail_depot(o.u.rail.depot.ddir)); + break; + case Item::Type::RAIL_STATION_PART: + add_tile(otile, ObjectTileHighlight::make_rail_station(o.u.rail.station_part.axis)); + break; + case Item::Type::RAIL_SIGNAL: + add_tile(otile, ObjectTileHighlight::make_rail_signal(o.u.rail.signal.pos, o.u.rail.signal.type, o.u.rail.signal.variant)); + break; + case Item::Type::RAIL_STATION: + break; + default: + NOT_REACHED(); + } + } + return res; +} + +sp Blueprint::Rotate() { + static const Trackdir ROTATE_TRACKDIR[] = { + TRACKDIR_Y_SE, TRACKDIR_X_SW, + TRACKDIR_RIGHT_S, TRACKDIR_LEFT_S, + TRACKDIR_UPPER_W, TRACKDIR_LOWER_W, + TRACKDIR_RVREV_SE, TRACKDIR_RVREV_SW, + TRACKDIR_Y_NW, TRACKDIR_X_NE, + TRACKDIR_RIGHT_N, TRACKDIR_LEFT_N, + TRACKDIR_UPPER_E, TRACKDIR_LOWER_E, + TRACKDIR_RVREV_NW, TRACKDIR_RVREV_NE + }; + static const uint ROTATE_SIGNAL_POS[] = { + // 5, 4, 7, 6, + // 2, 3, 0, 1, + // 11, 10, 8, 9 + 5, 4, 7, 6, + 2, 3, 0, 1, + 10, 11, 9, 8 + }; + auto res = std::make_shared(); + + auto rotate = [](TileIndexDiffC td) -> TileIndexDiffC { + return TileIndexDiffC {td.y, -td.x}; + }; + + auto rotate_dir = [](DiagDirection ddir) -> DiagDirection { + return ChangeDiagDir(ddir, DIAGDIRDIFF_90RIGHT); + }; + + for (auto &o: this->items) { + auto odiff = rotate(o.tdiff); + Blueprint::Item bi(o.type, odiff); + switch(o.type) { + case Item::Type::RAIL_TRACK: + bi.u.rail.track.length = o.u.rail.track.length; + bi.u.rail.track.start_dir = ROTATE_TRACKDIR[o.u.rail.track.start_dir]; + break; + case Item::Type::RAIL_BRIDGE: + bi.u.rail.bridge.type = o.u.rail.bridge.type; + bi.u.rail.bridge.ddir = rotate_dir(o.u.rail.bridge.ddir); + bi.u.rail.bridge.other_end = rotate(o.u.rail.bridge.other_end); + break; + case Item::Type::RAIL_TUNNEL: + bi.u.rail.tunnel.ddir = rotate_dir(o.u.rail.tunnel.ddir); + bi.u.rail.tunnel.other_end = rotate(o.u.rail.tunnel.other_end); + break; + case Item::Type::RAIL_DEPOT: + bi.u.rail.depot.ddir = rotate_dir(o.u.rail.depot.ddir); + break; + case Item::Type::RAIL_STATION: + bi.u.rail.station.id = o.u.rail.station.id; + bi.u.rail.station.has_part = o.u.rail.station.has_part; + break; + case Item::Type::RAIL_STATION_PART: + bi.u.rail.station_part.id = o.u.rail.station_part.id; + bi.u.rail.station_part.axis = OtherAxis(o.u.rail.station_part.axis); + break; + case Item::Type::RAIL_SIGNAL: + bi.u.rail.signal.pos = ROTATE_SIGNAL_POS[o.u.rail.signal.pos]; // TODO rotate + bi.u.rail.signal.type = o.u.rail.signal.type; + bi.u.rail.signal.variant = o.u.rail.signal.variant; + break; + default: + NOT_REACHED(); + } + res->Add(bi); + } + return res; +} + + +static void BlueprintAddSignals(sp &blueprint, TileIndex tile, TileIndexDiffC tdiff) { + // reference: DrawSignals @ rail_cmd.cpp + + auto add = [&](Track track, uint x, uint pos) { + if (!IsSignalPresent(tile, x)) return; + Blueprint::Item bi(Blueprint::Item::Type::RAIL_SIGNAL, tdiff); + bi.u.rail.signal.pos = pos; + bi.u.rail.signal.type = GetSignalType(tile, track); + bi.u.rail.signal.variant = GetSignalVariant(tile, track); + blueprint->Add(bi); + }; + auto rails = GetTrackBits(tile); + if (!(rails & TRACK_BIT_Y)) { + if (!(rails & TRACK_BIT_X)) { + if (rails & TRACK_BIT_LEFT) { + add(TRACK_LEFT, 2, 0); + add(TRACK_LEFT, 3, 1); + } + if (rails & TRACK_BIT_RIGHT) { + add(TRACK_RIGHT, 0, 2); + add(TRACK_RIGHT, 1, 3); + } + if (rails & TRACK_BIT_UPPER) { + add(TRACK_UPPER, 3, 4); + add(TRACK_UPPER, 2, 5); + } + if (rails & TRACK_BIT_LOWER) { + add(TRACK_LOWER, 1, 6); + add(TRACK_LOWER, 0, 7); + } + } else { + add(TRACK_X, 3, 8); + add(TRACK_X, 2, 9); + } + } else { + add(TRACK_Y, 3, 10); + add(TRACK_Y, 2, 11); + } +} + +// void BlueprintCopyArea(TileIndex start, TileIndex end) { +// TileArea ta{start, end}; + +// auto blueprint = std::make_shared(); +// _active_blueprint = std::make_pair(start, blueprint); + +// TILE_AREA_LOOP(tile, ta) { +// TileIndexDiff td = tile - start; +// switch (GetTileType(tile)) { +// case MP_STATION: +// if (IsRailStation(tile)) +// blueprint->Add(td, ObjectTileHighlight::make_rail_station(GetRailStationAxis(tile))); +// break; + +// case MP_RAILWAY: +// switch (GetRailTileType(tile)) { +// case RAIL_TILE_DEPOT: +// blueprint->Add(td, ObjectTileHighlight::make_rail_depot(GetRailDepotDirection(tile))); +// break; +// case RAIL_TILE_SIGNALS: +// BlueprintAddSignals(blueprint, tile, td); +// FALLTHROUGH; +// case RAIL_TILE_NORMAL: { +// auto tb = GetTrackBits(tile); +// for (Track track = TRACK_BEGIN; track < TRACK_END; track++) { +// if (!HasBit(tb, track)) continue; +// blueprint->Add(td, ObjectTileHighlight::make_rail_track(track)); +// } +// break; +// } +// default: +// NOT_REACHED(); +// } +// break; +// case MP_TUNNELBRIDGE: { +// if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) break; +// if (IsTunnel(tile)) break; +// auto ddir = GetTunnelBridgeDirection(tile); +// auto delta = TileOffsByDiagDir(ddir); +// auto other = GetOtherTunnelBridgeEnd(tile); +// auto axis = DiagDirToAxis(ddir); +// auto type = GetBridgeType(tile); +// blueprint->Add(td, ObjectTileHighlight::make_rail_bridge_head(ddir, other, type)); +// for (auto t = tile + delta; t != other; t += delta) { +// // blueprint->Add(t - start, ObjectTileHighlight::make_rail_tunnelbridge(axis)); +// } +// blueprint->Add(other - start, ObjectTileHighlight::make_rail_bridge_head(ReverseDiagDir(ddir), INVALID_TILE, type)); +// break; +// } +// default: +// break; +// } +// } +// } + +static void BlueprintAddTracks(sp &blueprint, TileIndex tile, TileIndexDiffC tdiff, TileArea &area, + std::map &track_tiles) { + // tilearea is iterated by x and y so chose direction to go in uniterated area + static const Trackdir _track_iterate_dir[TRACK_END] = { TRACKDIR_X_SW, TRACKDIR_Y_SE, TRACKDIR_UPPER_E, TRACKDIR_LOWER_E, TRACKDIR_LEFT_S, TRACKDIR_RIGHT_S}; + + for (Track track = TRACK_BEGIN; track < TRACK_END; track++) { + TileIndex c_tile = tile; + TileIndex end_tile = INVALID_TILE; + uint16 length = 0; + Track c_track = track; + Trackdir c_tdir = _track_iterate_dir[track]; + // fprintf(stderr, "TTTTTT %u %u %u\n", c_tile, c_track, GetTrackBits(c_tile)); + while (IsPlainRailTile(c_tile) && HasBit(GetTrackBits(c_tile), c_track)) { + if (HasBit(track_tiles[c_tile], c_track)) break; + length++; + SetBit(track_tiles[c_tile], c_track); + end_tile = c_tile; + c_tile = TileAddByDiagDir(c_tile, TrackdirToExitdir(c_tdir)); + if (!area.Contains(c_tile)) break; + c_tdir = NextTrackdir(c_tdir); + c_track = TrackdirToTrack(c_tdir); + // fprintf(stderr, "TTTTTTI %u %u %u\n", c_tile, c_track, GetTrackBits(c_tile)); + } + if (end_tile == INVALID_TILE) continue; + Blueprint::Item bi(Blueprint::Item::Type::RAIL_TRACK, tdiff); + bi.u.rail.track.length = length; + bi.u.rail.track.start_dir = _track_iterate_dir[track]; + // fprintf(stderr, "TTTTTTEE %u %u %u\n", tdiff, bi.u.rail.track.end_diff, bi.u.rail.track.start_dir); + blueprint->Add(bi); + } +} + +void BlueprintCopyArea(TileIndex start, TileIndex end) { + // if (start > end) Swap(start, end); + TileArea ta{start, end}; + start = ta.tile; + + auto blueprint = std::make_shared(); + _active_blueprint = std::make_pair(start, blueprint); + + std::map track_tiles; + std::multimap station_tiles; + std::set stations; + + TILE_AREA_LOOP(tile, ta) { + TileIndexDiffC td = TileIndexToTileIndexDiffC(tile, start); + switch (GetTileType(tile)) { + case MP_STATION: + if (IsRailStation(tile)) stations.insert(GetStationIndex(tile)); + break; + + case MP_RAILWAY: + switch (GetRailTileType(tile)) { + case RAIL_TILE_DEPOT: { + Blueprint::Item bi(Blueprint::Item::Type::RAIL_DEPOT, td); + bi.u.rail.depot.ddir = GetRailDepotDirection(tile); + blueprint->Add(bi); + break; + } + case RAIL_TILE_SIGNALS: + BlueprintAddSignals(blueprint, tile, td); + FALLTHROUGH; + case RAIL_TILE_NORMAL: + BlueprintAddTracks(blueprint, tile, td, ta, track_tiles); + break; + default: + NOT_REACHED(); + } + break; + case MP_TUNNELBRIDGE: { + if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) break; + auto other = GetOtherTunnelBridgeEnd(tile); + if (!ta.Contains(other)) break; + if (other < tile) break; + Blueprint::Item bi(Blueprint::Item::Type::RAIL_TUNNEL, td); + if (!IsTunnel(tile)) { + bi.type = Blueprint::Item::Type::RAIL_BRIDGE; + bi.u.rail.bridge.type = GetBridgeType(tile); + bi.u.rail.bridge.ddir = GetTunnelBridgeDirection(tile); + bi.u.rail.bridge.other_end = TileIndexToTileIndexDiffC(other, start); + } else { + bi.u.rail.tunnel.ddir = GetTunnelBridgeDirection(tile); + bi.u.rail.tunnel.other_end = TileIndexToTileIndexDiffC(other, start); + } + blueprint->Add(bi); + break; + } + default: + break; + } + } + + for (auto sid : stations) { + auto st = Station::Get(sid); + + if (!ta.Contains(st->xy)) continue; + + TileArea sta(TileXY(st->rect.left, st->rect.top), TileXY(st->rect.right, st->rect.bottom)); + bool in_area = true; + bool sign_part = false; + std::vector tiles; + TILE_AREA_LOOP(tile, sta) { + if (!IsTileType(tile, MP_STATION) || GetStationIndex(tile) != sid || !IsRailStation(tile)) continue; + if (!ta.Contains(tile)) { + in_area = false; + break; + } + tiles.push_back(tile); + if (tile == st->xy) sign_part = true; + } + + if (!in_area) continue; + + Blueprint::Item bi(Blueprint::Item::Type::RAIL_STATION, TileIndexToTileIndexDiffC(st->xy, start)); + bi.u.rail.station.id = sid; + bi.u.rail.station.has_part = sign_part; + blueprint->Add(bi); + + for (auto tile : tiles) { + Blueprint::Item bi(Blueprint::Item::Type::RAIL_STATION_PART, TileIndexToTileIndexDiffC(tile, start)); + bi.u.rail.station_part.id = sid; + bi.u.rail.station_part.axis = GetRailStationAxis(tile); + blueprint->Add(bi); + } + } +} + + +void UpdateBlueprintTileSelection(Point pt, TileIndex tile) { + if (tile == INVALID_TILE || _active_blueprint.first == INVALID_TILE || !_active_blueprint.second) { + _thd.cm_new = ObjectHighlight{}; + return; + } + _thd.cm_new = ObjectHighlight::make_blueprint(tile, _active_blueprint.second); +} + +void ResetActiveBlueprint() { + _active_blueprint = std::make_pair(INVALID_TILE, nullptr); +} + +void SetBlueprintHighlight(const TileInfo *ti, TileHighlight &th) { + if (_active_blueprint.first == INVALID_TILE || !_active_blueprint.second) + return; + + TileIndexDiffC td = TileIndexToTileIndexDiffC(ti->tile, _active_blueprint.first); + if (_active_blueprint.second->HasTile(td)) { + th.ground_pal = th.structure_pal = PALETTE_TINT_BLUE; + } +} + +void BuildBlueprint(sp &blueprint, TileIndex start) { + TileIndex last_tile; + uint32 last_p1, last_p2, last_cmd = CMD_END; + for (auto &item : blueprint->items) { + switch (item.type) { + case Blueprint::Item::Type::RAIL_TRACK: { + auto start_tile = AddTileIndexDiffCWrap(start, item.tdiff); + auto end_tile = start_tile; + auto tdir = item.u.rail.track.start_dir; + for (auto i = 1; i < item.u.rail.track.length; i++) { + end_tile = TileAddByDiagDir(end_tile, TrackdirToExitdir(tdir)); + tdir = NextTrackdir(tdir); + } + DoCommandP( + last_tile = start_tile, + last_p1 = end_tile, + last_p2 = (uint32)(_cur_railtype | (TrackdirToTrack(item.u.rail.track.start_dir) << 6)), + last_cmd = CMD_BUILD_RAILROAD_TRACK + ); + break; + } + case Blueprint::Item::Type::RAIL_DEPOT: + DoCommandP( + AddTileIndexDiffCWrap(start, item.tdiff), + _cur_railtype, + item.u.rail.depot.ddir, + CMD_BUILD_TRAIN_DEPOT + ); + break; + case Blueprint::Item::Type::RAIL_TUNNEL: + // TODO check that other end is where it should be + DoCommandP( + AddTileIndexDiffCWrap(start, item.tdiff), + _cur_railtype | (TRANSPORT_RAIL << 8), + 0, + CMD_BUILD_TUNNEL + ); + break; + case Blueprint::Item::Type::RAIL_BRIDGE: { + DoCommandP( + AddTileIndexDiffCWrap(start, item.u.rail.bridge.other_end), + AddTileIndexDiffCWrap(start, item.tdiff), + item.u.rail.bridge.type | (_cur_railtype << 8) | (TRANSPORT_RAIL << 15), + CMD_BUILD_BRIDGE + ); + break; + } + case Blueprint::Item::Type::RAIL_STATION: { + // TODO station types + TileIndex tile = AddTileIndexDiffCWrap(start, item.tdiff); + DoCommandWithCallback( + tile, + _cur_railtype | (1 << 8) | (1 << 16) | (1 << 24), + NEW_STATION << 16, + CMD_BUILD_RAIL_STATION, + [&blueprint, tile, start, sign_part=item.u.rail.station.has_part, sid=item.u.rail.station.id] (bool res) { + if (!res) return; + StationID station_id = GetStationIndex(tile); + for (auto &item : blueprint->items) { + if (item.type != Blueprint::Item::Type::RAIL_STATION_PART) continue; + if (item.u.rail.station_part.id != sid) continue; + DoCommandP( + AddTileIndexDiffCWrap(start, item.tdiff), + _cur_railtype | (item.u.rail.station_part.axis << 6) | (1 << 8) | (1 << 16) | (1 << 24), + station_id << 16, + CMD_BUILD_RAIL_STATION + ); + } + if (!sign_part) DoCommandP(tile, 0, 0, CMD_REMOVE_FROM_RAIL_STATION); + } + ); + break; + } + default: + break; + } + } + + auto signal_callback = [start, &blueprint](bool res) { + static const Track SIGNAL_POS_TRACK[] = { + TRACK_LEFT, TRACK_LEFT, TRACK_RIGHT, TRACK_RIGHT, + TRACK_UPPER, TRACK_UPPER, TRACK_LOWER, TRACK_LOWER, + TRACK_X, TRACK_X, TRACK_Y, TRACK_Y, + }; + for (auto &item : blueprint->items) { + if (item.type != Blueprint::Item::Type::RAIL_SIGNAL) continue; + DoCommandP( + AddTileIndexDiffCWrap(start, item.tdiff), + SIGNAL_POS_TRACK[item.u.rail.signal.pos] | (item.u.rail.signal.variant << 4) | (item.u.rail.signal.type << 5) + | ((item.u.rail.signal.pos % 2) << 15), + 0, + CMD_BUILD_SIGNALS + ); + } + }; + if (last_cmd != CMD_END) { // there can't be any signals if there are no rails + if (_networking) AddCommandCallback(last_tile, last_p1, last_p2, last_cmd, signal_callback); + else signal_callback(true); + } +} + +void RotateActiveBlueprint() { + _active_blueprint.second = _active_blueprint.second->Rotate(); +} + +void BuildActiveBlueprint(TileIndex start) { + BuildBlueprint(_active_blueprint.second, start); +} + + +} // namespace citymania diff --git a/src/citymania/cm_blueprint.hpp b/src/citymania/cm_blueprint.hpp new file mode 100644 index 0000000000..242f55027f --- /dev/null +++ b/src/citymania/cm_blueprint.hpp @@ -0,0 +1,21 @@ +#ifndef CM_BLUEPRINT_HPP +#define CM_BLUEPRINT_HPP + +#include "cm_highlight.hpp" + +namespace citymania { + +void BlueprintCopyArea(TileIndex start, TileIndex end); +void ResetActiveBlueprint(); +void SetBlueprintHighlight(const TileInfo *ti, TileHighlight &th); + + +void UpdateBlueprintTileSelection(Point pt, TileIndex tile); +void BuildActiveBlueprint(TileIndex start); +void RotateActiveBlueprint(); + +void CommandExecuted(bool res, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd); + +} //- namespace citymania + +#endif diff --git a/src/citymania/cm_highlight.cpp b/src/citymania/cm_highlight.cpp index 0e5b477f9a..ed122b2c65 100644 --- a/src/citymania/cm_highlight.cpp +++ b/src/citymania/cm_highlight.cpp @@ -1,10 +1,13 @@ #include "../stdafx.h" #include "cm_highlight.hpp" + +#include "cm_blueprint.hpp" #include "cm_main.hpp" #include "cm_station_gui.hpp" #include "../core/math_func.hpp" +#include "../table/bridge_land.h" #include "../command_func.h" #include "../house.h" #include "../industry.h" @@ -71,27 +74,53 @@ const byte _tileh_to_sprite[32] = { 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 17, 0, 15, 18, 0, }; - -ObjectTileHighlight ObjectTileHighlight::make_depot(DiagDirection ddir) { +ObjectTileHighlight ObjectTileHighlight::make_rail_depot(DiagDirection ddir) { auto oh = ObjectTileHighlight(Type::RAIL_DEPOT); - oh.u.depot.ddir = ddir; + oh.u.rail.depot.ddir = ddir; return oh; } -ObjectTileHighlight ObjectTileHighlight::make_rail(Track track) { +ObjectTileHighlight ObjectTileHighlight::make_rail_track(Track track) { auto oh = ObjectTileHighlight(Type::RAIL_TRACK); oh.u.rail.track = track; return oh; } +ObjectTileHighlight ObjectTileHighlight::make_rail_station(Axis axis) { + auto oh = ObjectTileHighlight(Type::RAIL_STATION); + oh.u.rail.station.axis = axis; + return oh; +} + +ObjectTileHighlight ObjectTileHighlight::make_rail_signal(uint pos, SignalType type, SignalVariant variant) { + auto oh = ObjectTileHighlight(Type::RAIL_SIGNAL); + oh.u.rail.signal.pos = pos; + oh.u.rail.signal.type = type; + oh.u.rail.signal.variant = variant; + return oh; +} + +ObjectTileHighlight ObjectTileHighlight::make_rail_bridge_head(DiagDirection ddir, BridgeType type) { + auto oh = ObjectTileHighlight(Type::RAIL_BRIDGE_HEAD); + oh.u.rail.bridge_head.ddir = ddir; + oh.u.rail.bridge_head.type = type; + return oh; +} + +ObjectTileHighlight ObjectTileHighlight::make_rail_tunnel_head(DiagDirection ddir) { + auto oh = ObjectTileHighlight(Type::RAIL_TUNNEL_HEAD); + oh.u.rail.tunnel_head.ddir = ddir; + 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; + return (this->tile == oh.tile && this->ddir == oh.ddir && this->blueprint == oh.blueprint); + // switch (this->type) { + // case Type::RAIL_DEPOT: return this->tile == oh.tile && this->ddir == oh.ddir; + // default: return true; + // } + // return true; } bool ObjectHighlight::operator!=(const ObjectHighlight& oh) { @@ -100,9 +129,16 @@ bool ObjectHighlight::operator!=(const ObjectHighlight& 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; + auto oh = ObjectHighlight{ObjectHighlight::Type::RAIL_DEPOT}; + oh.tile = tile; + oh.ddir = ddir; + return oh; +} + +ObjectHighlight ObjectHighlight::make_blueprint(TileIndex tile, sp blueprint) { + auto oh = ObjectHighlight{ObjectHighlight::Type::BLUEPRINT}; + oh.tile = tile; + oh.blueprint = blueprint; return oh; } @@ -118,7 +154,7 @@ void ObjectHighlight::PlaceExtraDepotRail(TileIndex tile, DiagDirection dir, Tra 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))); + this->tiles.insert(std::make_pair(tile, ObjectTileHighlight::make_rail_track(track))); } /** Additional pieces of track to add at the entrance of a depot. */ @@ -139,16 +175,20 @@ 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); + auto dir = this->ddir; + this->tiles.insert(std::make_pair(this->tile, ObjectTileHighlight::make_rail_depot(dir))); + auto tile = this->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]); + this->PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir], _place_depot_extra_track[dir]); + this->PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir + 4], _place_depot_extra_track[dir + 4]); + this->PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir + 8], _place_depot_extra_track[dir + 8]); } break; } + case Type::BLUEPRINT: + if (this->blueprint && this->tile != INVALID_TILE) + this->tiles = this->blueprint->GetTiles(this->tile); + break; default: break; } @@ -194,24 +234,208 @@ void DrawTrainDepotSprite(const TileInfo *ti, RailType railtype, DiagDirection d DrawRailTileSeq(ti, dts, TO_INVALID, offset, 0, PALETTE_TINT_WHITE); } +void DrawTrainStationSprite(const TileInfo *ti, RailType railtype, Axis axis) { + int32 total_offset = 0; + PaletteID pal = COMPANY_SPRITE_COLOUR(_local_company); + const DrawTileSprites *t = GetStationTileLayout(STATION_RAIL, (axis == AXIS_X ? 0 : 1)); + const RailtypeInfo *rti = nullptr; + + if (railtype != INVALID_RAILTYPE) { + rti = GetRailTypeInfo(railtype); + total_offset = rti->GetRailtypeSpriteOffset(); + } + + DrawAutorailSelection(ti, (axis == AXIS_X ? HT_DIR_X : HT_DIR_Y), PAL_NONE); + + // if (roadtype != INVALID_ROADTYPE) { + // const RoadTypeInfo* rti = GetRoadTypeInfo(roadtype); + // if (image >= 4) { + // /* Drive-through stop */ + // uint sprite_offset = 5 - image; + + // /* Road underlay takes precedence over tram */ + // if (rti->UsesOverlay()) { + // SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_GROUND); + // DrawSprite(ground + sprite_offset, PAL_NONE, x, y); + + // SpriteID overlay = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_OVERLAY); + // if (overlay) DrawSprite(overlay + sprite_offset, PAL_NONE, x, y); + // } else if (RoadTypeIsTram(roadtype)) { + // DrawSprite(SPR_TRAMWAY_TRAM + sprite_offset, PAL_NONE, x, y); + // } + // } else { + // /* Drive-in stop */ + // if (RoadTypeIsRoad(roadtype) && rti->UsesOverlay()) { + // SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_ROADSTOP); + // DrawSprite(ground + image, PAL_NONE, x, y); + // } + // } + // } + + /* Default waypoint has no railtype specific sprites */ + // DrawRailTileSeq(ti, t, TO_INVALID, (st == STATION_WAYPOINT ? 0 : total_offset), 0, PALETTE_TINT_WHITE); + DrawRailTileSeq(ti, t, TO_INVALID, total_offset, 0, PALETTE_TINT_WHITE); +} + +enum SignalOffsets { // from rail_cmd.cpp + SIGNAL_TO_SOUTHWEST, + SIGNAL_TO_NORTHEAST, + SIGNAL_TO_SOUTHEAST, + SIGNAL_TO_NORTHWEST, + SIGNAL_TO_EAST, + SIGNAL_TO_WEST, + SIGNAL_TO_SOUTH, + SIGNAL_TO_NORTH, +}; + +/** + * copied from rail_cmd.cpp + * Get surface height in point (x,y) + * On tiles with halftile foundations move (x,y) to a safe point wrt. track + */ +static uint GetSaveSlopeZ(uint x, uint y, Track track) +{ + switch (track) { + case TRACK_UPPER: x &= ~0xF; y &= ~0xF; break; + case TRACK_LOWER: x |= 0xF; y |= 0xF; break; + case TRACK_LEFT: x |= 0xF; y &= ~0xF; break; + case TRACK_RIGHT: x &= ~0xF; y |= 0xF; break; + default: break; + } + return GetSlopePixelZ(x, y); +} + +void DrawSignal(const TileInfo *ti, RailType railtype, uint pos, SignalType type, SignalVariant variant) { + // reference: DraawSingleSignal in rail_cmd.cpp + bool side; + switch (_settings_game.construction.train_signal_side) { + case 0: side = false; break; // left + case 2: side = true; break; // right + default: side = _settings_game.vehicle.road_side != 0; break; // driving side + } + static const Point SignalPositions[2][12] = { + { // Signals on the left side + /* LEFT LEFT RIGHT RIGHT UPPER UPPER */ + { 8, 5}, {14, 1}, { 1, 14}, { 9, 11}, { 1, 0}, { 3, 10}, + /* LOWER LOWER X X Y Y */ + {11, 4}, {14, 14}, {11, 3}, { 4, 13}, { 3, 4}, {11, 13} + }, { // Signals on the right side + /* LEFT LEFT RIGHT RIGHT UPPER UPPER */ + {14, 1}, {12, 10}, { 4, 6}, { 1, 14}, {10, 4}, { 0, 1}, + /* LOWER LOWER X X Y Y */ + {14, 14}, { 5, 12}, {11, 13}, { 4, 3}, {13, 4}, { 3, 11} + } + }; + + uint x = TileX(ti->tile) * TILE_SIZE + SignalPositions[side][pos].x; + uint y = TileY(ti->tile) * TILE_SIZE + SignalPositions[side][pos].y; + + static const Track pos_track[] = { + TRACK_LEFT, TRACK_LEFT, TRACK_RIGHT, TRACK_RIGHT, + TRACK_UPPER, TRACK_UPPER, TRACK_LOWER, TRACK_LOWER, + TRACK_X, TRACK_X, TRACK_Y, TRACK_Y, + }; + static const SignalOffsets pos_offset[] = { + SIGNAL_TO_NORTH, SIGNAL_TO_SOUTH, SIGNAL_TO_NORTH, SIGNAL_TO_SOUTH, + SIGNAL_TO_WEST, SIGNAL_TO_EAST, SIGNAL_TO_WEST, SIGNAL_TO_EAST, + SIGNAL_TO_SOUTHWEST, SIGNAL_TO_NORTHEAST, SIGNAL_TO_SOUTHEAST, SIGNAL_TO_NORTHWEST, + }; + + auto track = pos_track[pos]; + auto image = pos_offset[pos]; + static const SignalState condition = SIGNAL_STATE_GREEN; + + auto rti = GetRailTypeInfo(railtype); + SpriteID sprite = GetCustomSignalSprite(rti, ti->tile, type, variant, condition); + if (sprite != 0) { + sprite += image; + } else { + /* Normal electric signals are stored in a different sprite block than all other signals. */ + sprite = (type == SIGTYPE_NORMAL && variant == SIG_ELECTRIC) ? SPR_ORIGINAL_SIGNALS_BASE : SPR_SIGNALS_BASE - 16; + sprite += type * 16 + variant * 64 + image * 2 + condition + (type > SIGTYPE_LAST_NOPBS ? 64 : 0); + } + + AddSortableSpriteToDraw(sprite, PALETTE_TINT_WHITE, x, y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, GetSaveSlopeZ(x, y, track)); +} + +// copied from tunnelbridge_cmd.cpp +static inline const PalSpriteID *GetBridgeSpriteTable(int index, BridgePieces table) +{ + const BridgeSpec *bridge = GetBridgeSpec(index); + assert(table < BRIDGE_PIECE_INVALID); + if (bridge->sprite_table == nullptr || bridge->sprite_table[table] == nullptr) { + return _bridge_sprite_table[index][table]; + } else { + return bridge->sprite_table[table]; + } +} + +void DrawBridgeHead(const TileInfo *ti, RailType railtype, DiagDirection ddir, BridgeType type) { + auto rti = GetRailTypeInfo(railtype); + int base_offset = rti->bridge_offset; + const PalSpriteID *psid; + + /* HACK Wizardry to convert the bridge ramp direction into a sprite offset */ + base_offset += (6 - ddir) % 4; + + /* Table number BRIDGE_PIECE_HEAD always refers to the bridge heads for any bridge type */ + if (ti->tileh == SLOPE_FLAT) base_offset += 4; // sloped bridge head + psid = &GetBridgeSpriteTable(type, BRIDGE_PIECE_HEAD)[base_offset]; + + AddSortableSpriteToDraw(psid->sprite, PALETTE_TINT_WHITE, ti->x, ti->y, 16, 16, ti->tileh == SLOPE_FLAT ? 0 : 8, ti->z); + // DrawAutorailSelection(ti, (ddir == DIAGDIR_SW || ddir == DIAGDIR_NE ? HT_DIR_X : HT_DIR_Y), PAL_NONE); +} + +void DrawTunnelHead(const TileInfo *ti, RailType railtype, DiagDirection ddir) { + auto rti = GetRailTypeInfo(railtype); + + SpriteID image; + SpriteID railtype_overlay = 0; + + image = rti->base_sprites.tunnel; + if (rti->UsesOverlay()) { + /* Check if the railtype has custom tunnel portals. */ + railtype_overlay = GetCustomRailSprite(rti, ti->tile, RTSG_TUNNEL_PORTAL); + if (railtype_overlay != 0) image = SPR_RAILTYPE_TUNNEL_BASE; // Draw blank grass tunnel base. + } + + image += ddir * 2; + AddSortableSpriteToDraw(image, PALETTE_TINT_WHITE, ti->x, ti->y, 16, 16, 0, ti->z); +} + void ObjectHighlight::Draw(const TileInfo *ti) { this->UpdateTiles(); auto range = this->tiles.equal_range(ti->tile); + auto i=0; for (auto t = range.first; t != range.second; t++) { + i++; auto &oth = t->second; switch (oth.type) { case ObjectTileHighlight::Type::RAIL_DEPOT: - DrawTrainDepotSprite(ti, _cur_railtype, oth.u.depot.ddir); + DrawTrainDepotSprite(ti, _cur_railtype, oth.u.rail.depot.ddir); break; case ObjectTileHighlight::Type::RAIL_TRACK: { auto hs = (HighLightStyle)oth.u.rail.track; DrawAutorailSelection(ti, hs, PAL_NONE); break; } + case ObjectTileHighlight::Type::RAIL_STATION: + DrawTrainStationSprite(ti, _cur_railtype, oth.u.rail.station.axis); + break; + case ObjectTileHighlight::Type::RAIL_SIGNAL: + DrawSignal(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(ti, _cur_railtype, oth.u.rail.bridge_head.ddir, oth.u.rail.bridge_head.type); + break; + case ObjectTileHighlight::Type::RAIL_TUNNEL_HEAD: + DrawTunnelHead(ti, _cur_railtype, oth.u.rail.tunnel_head.ddir); + break; default: break; } } + // fprintf(stderr, "TILEH DRAW %d %d %d\n", ti->tile, (int)i, (int)this->tiles.size()); } @@ -525,6 +749,7 @@ TileHighlight GetTileHighlight(const TileInfo *ti) { } SetStationSelectionHighlight(ti, th); + SetBlueprintHighlight(ti, th); return th; } @@ -545,6 +770,8 @@ void DrawTileZoning(const TileInfo *ti, const TileHighlight &th) { bool DrawTileSelection(const TileInfo *ti, const TileHighlightType &tht) { _thd.cm.Draw(ti); + if (_thd.drawstyle == CM_HT_BLUEPRINT_PLACE) return true; + if ((_thd.drawstyle & HT_DRAG_MASK) == HT_RECT && _thd.outersize.x > 0) { // station selector, handled by DrawTileZoning return true; @@ -610,11 +837,14 @@ DiagDirection AutodetectRailObjectDirection(TileIndex tile, Point pt) { HighLightStyle UpdateTileSelection(HighLightStyle new_drawstyle) { _thd.cm_new = ObjectHighlight(ObjectHighlight::Type::NONE); - if ((_thd.place_mode & HT_DRAG_MASK) == HT_RECT && + auto pt = GetTileBelowCursor(); + auto tile = (pt.x == -1 ? INVALID_TILE : TileVirtXY(pt.x, pt.y)); + if (_thd.place_mode == CM_HT_BLUEPRINT_PLACE) { + UpdateBlueprintTileSelection(pt, tile); + new_drawstyle = CM_HT_BLUEPRINT_PLACE; + } else 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); diff --git a/src/citymania/cm_highlight_type.hpp b/src/citymania/cm_highlight_type.hpp index 81bbfb75ba..5af02dd8a0 100644 --- a/src/citymania/cm_highlight_type.hpp +++ b/src/citymania/cm_highlight_type.hpp @@ -1,36 +1,152 @@ #ifndef CITYMANIA_HIGHLIGHT_TYPE_HPP #define CITYMANIA_HIGHLIGHT_TYPE_HPP +#include "../bridge.h" #include "../direction_type.h" +#include "../map_func.h" +#include "../signal_type.h" +#include "../station_type.h" #include "../tile_cmd.h" #include "../tile_type.h" #include "../track_type.h" #include +#include +#include + namespace citymania { - class ObjectTileHighlight { public: enum class Type { - RAIL_DEPOT = 0, - RAIL_TRACK = 1, + BEGIN = 0, + RAIL_DEPOT = BEGIN, + RAIL_TRACK, + RAIL_STATION, + RAIL_SIGNAL, + RAIL_BRIDGE_HEAD, + RAIL_TUNNEL_HEAD, + END, }; Type type; union { struct { - DiagDirection ddir; - } depot; - struct { + struct { + DiagDirection ddir; + } depot; Track track; + struct { + Axis axis; + } station; + struct { + uint pos; + SignalType type; + SignalVariant variant; + } signal; + struct { + DiagDirection ddir; + TileIndex other_end; + BridgeType type; + } bridge_head; + struct { + DiagDirection ddir; + } tunnel_head; } rail; } u; ObjectTileHighlight(Type type): type{type} {} - static ObjectTileHighlight make_depot(DiagDirection ddir); - static ObjectTileHighlight make_rail(Track track); + static ObjectTileHighlight make_rail_depot(DiagDirection ddir); + static ObjectTileHighlight make_rail_track(Track track); + static ObjectTileHighlight make_rail_station(Axis axis); + static ObjectTileHighlight make_rail_signal(uint pos, SignalType type, SignalVariant variant); + static ObjectTileHighlight make_rail_bridge_head(DiagDirection ddir, BridgeType type); + static ObjectTileHighlight make_rail_tunnel_head(DiagDirection ddir); +}; + +class TileIndexDiffCCompare{ +public: + bool operator()(const TileIndexDiffC &a, const TileIndexDiffC &b) { + if (a.x < b.x) return true; + if (a.x == b.x && a.y < b.y) return true; + return false; + } +}; + +class Blueprint { +public: + class Item { + public: + enum class Type { + BEGIN = 0, + RAIL_DEPOT = BEGIN, + RAIL_TRACK, + RAIL_STATION, + RAIL_STATION_PART, + RAIL_SIGNAL, + RAIL_BRIDGE, + RAIL_TUNNEL, + END, + }; + Type type; + TileIndexDiffC tdiff; + union { + struct { + struct { + DiagDirection ddir; + } depot; + struct { + uint16 length; + Trackdir start_dir; + } track; + struct { + StationID id; + bool has_part; + } station; + struct { + Axis axis; + StationID id; + } station_part; + struct { + uint pos; + SignalType type; + SignalVariant variant; + } signal; + struct { + DiagDirection ddir; + TileIndexDiffC other_end; + BridgeType type; + } bridge; + struct { + DiagDirection ddir; + TileIndexDiffC other_end; + } tunnel; + } rail; + } u; + Item(Type type, TileIndexDiffC tdiff) + : type{type}, tdiff{tdiff} {} + }; + + std::vector items; + std::set tiles; + + Blueprint() {} + + void Clear() { + this->items.clear(); + this->tiles.clear(); + } + + void Add(Item item); + + bool HasTile(TileIndexDiffC tdiff) { + return (this->tiles.find(tdiff) != this->tiles.end()); + } + + sp Rotate(); + + std::multimap GetTiles(TileIndex tile); }; class ObjectHighlight { @@ -38,16 +154,14 @@ public: enum class Type { NONE = 0, RAIL_DEPOT = 1, + BLUEPRINT = 2, }; protected: Type type; - union { - struct { - TileIndex tile; - DiagDirection ddir; - } depot; - } u; + TileIndex tile = INVALID_TILE; + DiagDirection ddir = INVALID_DIAGDIR; + sp blueprint = nullptr; bool tiles_updated = false; std::multimap tiles; @@ -55,11 +169,12 @@ protected: 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; } + ObjectHighlight(Type type = Type::NONE): type{type} {} bool operator==(const ObjectHighlight& oh); bool operator!=(const ObjectHighlight& oh); static ObjectHighlight make_depot(TileIndex tile, DiagDirection ddir); + static ObjectHighlight make_blueprint(TileIndex tile, sp blueprint); void Draw(const TileInfo *ti); void MarkDirty(); diff --git a/src/citymania/copypaste.cpp b/src/citymania/copypaste.cpp deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/citymania/copypaste.hpp b/src/citymania/copypaste.hpp deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/command.cpp b/src/command.cpp index f0b33d501d..b477759515 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -32,6 +32,7 @@ #include "table/strings.h" #include "citymania/cm_hotkeys.hpp" +#include "citymania/cm_blueprint.hpp" #include "safeguards.h" @@ -610,6 +611,8 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallbac if (!estimate_only && !only_sending && callback != nullptr) { callback(res, tile, p1, p2, cmd); } + if (!estimate_only && !only_sending) + citymania::CommandExecuted(res.Succeeded(), tile, p1, p2, cmd); return res.Succeeded(); } diff --git a/src/lang/english.txt b/src/lang/english.txt index 150f84c13e..b4599caac9 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5545,3 +5545,5 @@ STR_CM_CONFIG_SETTING_SHADED_TREES_SERVER : As server STR_CM_CONFIG_SETTING_SHOW_APM : Show APM counter: {STRING2} STR_CM_CONFIG_SETTING_SHOW_APM_HELPTEXT : Adds APM (actions per minute) counter to the statusbar. STR_CM_STATUSBAR_APM : {WHITE}APM: {NUM} AVG: {NUM} + +STR_CM_RAIL_TOOLBAR_TOOLTIP_BLUEPRINT : {BLACK}Rail blueprint tool (copy-paste) diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp index 613e80c5f1..a622c6f7b5 100644 --- a/src/rail_gui.cpp +++ b/src/rail_gui.cpp @@ -38,6 +38,7 @@ #include "widgets/rail_widget.h" +#include "citymania/cm_blueprint.hpp" #include "citymania/cm_hotkeys.hpp" #include "citymania/cm_station_gui.hpp" @@ -768,6 +769,11 @@ struct BuildRailToolbarWindow : Window { this->last_user_action = widget; break; + case CM_WID_RAT_BLUEPRINT: + HandlePlacePushButton(this, CM_WID_RAT_BLUEPRINT, SPR_CURSOR_RAIL_STATION, HT_RECT); + this->last_user_action = widget; + break; + case WID_RAT_REMOVE: _remove_button_clicked = citymania::RailToolbar_RemoveModChanged(this, _cm_invert_remove, _remove_button_clicked, true); // BuildRailClick_Remove(this); @@ -871,6 +877,15 @@ struct BuildRailToolbarWindow : Window { PlaceRail_Bridge(tile, this); break; + case CM_WID_RAT_BLUEPRINT: + VpStartPlaceSizing(tile, VPM_X_AND_Y, CM_DDSP_BLUEPRINT_AREA); + break; + + case CM_WID_RAT_BLUEPRINT_PLACE: + citymania::BuildActiveBlueprint(tile); + ResetObjectToPlace(); + break; + case WID_RAT_BUILD_TUNNEL: DoCommandP(tile, _cur_railtype | (TRANSPORT_RAIL << 8), 0, CMD_BUILD_TUNNEL | CMD_MSG(STR_ERROR_CAN_T_BUILD_TUNNEL_HERE), CcBuildRailTunnel); break; @@ -940,6 +955,12 @@ struct BuildRailToolbarWindow : Window { } } break; + + case CM_DDSP_BLUEPRINT_AREA: + SetObjectToPlace(SPR_CURSOR_RAIL_STATION, PAL_NONE, CM_HT_BLUEPRINT_PLACE, this->window_class, this->window_number); + citymania::BlueprintCopyArea(start_tile, end_tile); + this->last_user_action = CM_WID_RAT_BLUEPRINT_PLACE; + break; } } } @@ -960,6 +981,7 @@ struct BuildRailToolbarWindow : Window { DeleteWindowByClass(WC_BUILD_BRIDGE); citymania::AbortStationPlacement(); + citymania::ResetActiveBlueprint(); } void OnPlacePresize(Point pt, TileIndex tile) override @@ -970,6 +992,10 @@ struct BuildRailToolbarWindow : Window { EventState CM_OnRemoveModStateChange() override { + if (this->last_user_action == CM_WID_RAT_BLUEPRINT_PLACE) { + citymania::RotateActiveBlueprint(); + return ES_HANDLED; + } auto new_remove = citymania::RailToolbar_RemoveModChanged(this, _cm_invert_remove, _remove_button_clicked, false); if (new_remove != _remove_button_clicked) { _remove_button_clicked = new_remove; @@ -1058,6 +1084,8 @@ static const NWidgetPart _nested_build_rail_widgets[] = { SetFill(0, 1), SetMinimalSize(42, 22), SetDataTip(SPR_IMG_BRIDGE, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_BRIDGE), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_BUILD_TUNNEL), SetFill(0, 1), SetMinimalSize(20, 22), SetDataTip(SPR_IMG_TUNNEL_RAIL, STR_RAIL_TOOLBAR_TOOLTIP_BUILD_RAILROAD_TUNNEL), + NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, CM_WID_RAT_BLUEPRINT), + SetFill(0, 1), SetMinimalSize(20, 22), SetDataTip(SPR_IMG_BUY_LAND, STR_CM_RAIL_TOOLBAR_TOOLTIP_BLUEPRINT), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_REMOVE), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_REMOVE, STR_RAIL_TOOLBAR_TOOLTIP_TOGGLE_BUILD_REMOVE_FOR), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_CONVERT_RAIL), diff --git a/src/tilehighlight_type.h b/src/tilehighlight_type.h index d1b149b786..717342857f 100644 --- a/src/tilehighlight_type.h +++ b/src/tilehighlight_type.h @@ -29,6 +29,7 @@ enum HighLightStyle { HT_VEHICLE = 0x100, ///< vehicle is accepted as target as well (bitmask) HT_DIAGONAL = 0x200, ///< Also allow 'diagonal rectangles'. Only usable in combination with #HT_RECT or #HT_POINT. HT_POLY = 0x400, ///< polyline mode; connect highlighted track with previous one + CM_HT_BLUEPRINT_PLACE = 0x800, ///< CityMania blueprint placemment HT_DRAG_MASK = 0x0F8, ///< Mask for the tile drag-type modes. /* lower bits (used with HT_LINE and HT_RAIL): diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 1a66d530f0..49ccdf062f 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -261,6 +261,7 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u TileIndex tile_start = p1; TileIndex tile_end = end_tile; + fprintf(stderr, "BRIDGE %d %d %d %d\n", tile_start, tile_end, bridge_type, railtype); if (company == OWNER_DEITY) { if (transport_type != TRANSPORT_ROAD) return CMD_ERROR; const Town *town = CalcClosestTownFromTile(tile_start); diff --git a/src/viewport.cpp b/src/viewport.cpp index e6a5cc6623..4be4fdc4d0 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -2415,7 +2415,7 @@ bool HandleViewportClicked(const ViewPort *vp, int x, int y, bool double_click) } /* Vehicle placement mode already handled above. */ - if ((_thd.place_mode & HT_DRAG_MASK) != HT_NONE) { + if ((_thd.place_mode & HT_DRAG_MASK) != HT_NONE || _thd.place_mode == CM_HT_BLUEPRINT_PLACE) { if (_thd.place_mode & HT_POLY) { /* In polyline mode double-clicking on a single white line, finishes current polyline. * If however the user double-clicks on a line that has a white and a blue section, diff --git a/src/viewport_type.h b/src/viewport_type.h index def0ade89f..dbb87a5a40 100644 --- a/src/viewport_type.h +++ b/src/viewport_type.h @@ -142,6 +142,8 @@ enum ViewportDragDropSelectionProcess { DDSP_REMOVE_BUSSTOP, ///< Road stop removal (buses) DDSP_REMOVE_TRUCKSTOP, ///< Road stop removal (trucks) DDSP_CONVERT_ROAD, ///< Road conversion + + CM_DDSP_BLUEPRINT_AREA, ///< Blueprint area to copy selection }; diff --git a/src/widgets/rail_widget.h b/src/widgets/rail_widget.h index b0dc868c09..1419d9d485 100644 --- a/src/widgets/rail_widget.h +++ b/src/widgets/rail_widget.h @@ -27,8 +27,10 @@ enum RailToolbarWidgets { WID_RAT_BUILD_SIGNALS, ///< Build signals. WID_RAT_BUILD_BRIDGE, ///< Build a bridge. WID_RAT_BUILD_TUNNEL, ///< Build a tunnel. + CM_WID_RAT_BLUEPRINT, ///< CityMania rail blueprint tool. WID_RAT_REMOVE, ///< Bulldozer to remove rail. WID_RAT_CONVERT_RAIL, ///< Convert other rail to this type. + CM_WID_RAT_BLUEPRINT_PLACE, ///< CityMania rail blueprint tool placement (fake widget). }; /** Widgets of the #BuildRailStationWindow class. */