661 lines
26 KiB
C++
661 lines
26 KiB
C++
#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 <functional>
|
|
#include <map>
|
|
|
|
extern TileHighlightData _thd;
|
|
extern RailType _cur_railtype;
|
|
extern void GetStationLayout(byte *layout, uint numtracks, uint plat_len, const StationSpec *statspec);
|
|
|
|
namespace citymania {
|
|
|
|
|
|
std::pair<TileIndex, sp<Blueprint>> _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<TileIndex, uint32, uint32, uint32> CommandTuple;
|
|
typedef std::function<void(bool)> CommandCallback;
|
|
std::map<CommandTuple, std::vector<CommandCallback>> _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 & CMD_ID_MASK, callback);
|
|
DoCommandP(tile, p1, p2, cmd);
|
|
}
|
|
|
|
void DoCommandWithCallback(const CommandContainer &cc, CommandCallback callback) {
|
|
DoCommandWithCallback(cc.tile, cc.p1, cc.p2, cc.cmd, callback);
|
|
}
|
|
|
|
void CommandExecuted(bool res, TileIndex tile, uint32 p1, uint32 p2, uint32 cmd) {
|
|
CommandTuple ct {tile, p1, p2, cmd & CMD_ID_MASK};
|
|
auto p = _command_callbacks.find(ct);
|
|
if (p == _command_callbacks.end()) return;
|
|
for (auto &cb : p->second)
|
|
cb(res);
|
|
_command_callbacks.erase(p);
|
|
}
|
|
|
|
template<typename Func>
|
|
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 Blueprint::Add(TileIndex source_tile, Blueprint::Item item) {
|
|
this->items.push_back(item);
|
|
switch (item.type) {
|
|
case Item::Type::RAIL_TRACK: {
|
|
auto tdir = item.u.rail.track.start_dir;
|
|
for (auto i = 0; i < item.u.rail.track.length; i++) {
|
|
this->source_tiles.insert(source_tile);
|
|
source_tile = TileAddByDiagDir(source_tile, TrackdirToExitdir(tdir));
|
|
tdir = NextTrackdir(tdir);
|
|
}
|
|
break;
|
|
}
|
|
case Item::Type::RAIL_STATION_PART:
|
|
IterateStation(source_tile, item.u.rail.station_part.axis, item.u.rail.station_part.numtracks, item.u.rail.station_part.plat_len,
|
|
[&](TileIndex tile) {
|
|
this->source_tiles.insert(tile);
|
|
}
|
|
);
|
|
break;
|
|
case Item::Type::RAIL_BRIDGE:
|
|
case Item::Type::RAIL_TUNNEL:
|
|
this->source_tiles.insert(TILE_ADDXY(source_tile, item.u.rail.tunnel.other_end.x, item.u.rail.tunnel.other_end.y));
|
|
FALLTHROUGH;
|
|
case Item::Type::RAIL_DEPOT:
|
|
case Item::Type::RAIL_STATION:
|
|
this->source_tiles.insert(source_tile);
|
|
break;
|
|
case Item::Type::RAIL_SIGNAL: // tile is added for track anyway
|
|
break;
|
|
default:
|
|
NOT_REACHED();
|
|
}
|
|
}
|
|
|
|
CommandContainer GetBlueprintCommand(TileIndex start, const Blueprint::Item &item) {
|
|
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,
|
|
};
|
|
|
|
switch (item.type) {
|
|
case Blueprint::Item::Type::RAIL_TRACK: {
|
|
// TODO precalc
|
|
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);
|
|
}
|
|
return CommandContainer {
|
|
start_tile,
|
|
end_tile,
|
|
(uint32)(_cur_railtype) | ((uint32)TrackdirToTrack(item.u.rail.track.start_dir) << 6),
|
|
CMD_BUILD_RAILROAD_TRACK,
|
|
nullptr, ""
|
|
};
|
|
}
|
|
case Blueprint::Item::Type::RAIL_DEPOT:
|
|
return CommandContainer {
|
|
AddTileIndexDiffCWrap(start, item.tdiff),
|
|
_cur_railtype,
|
|
item.u.rail.depot.ddir,
|
|
CMD_BUILD_TRAIN_DEPOT,
|
|
nullptr, ""
|
|
};
|
|
case Blueprint::Item::Type::RAIL_TUNNEL:
|
|
// TODO check that other end is where it should be
|
|
return CommandContainer {
|
|
AddTileIndexDiffCWrap(start, item.tdiff),
|
|
(uint32)_cur_railtype | ((uint32)TRANSPORT_RAIL << 8),
|
|
0,
|
|
CMD_BUILD_TUNNEL,
|
|
nullptr, ""
|
|
};
|
|
case Blueprint::Item::Type::RAIL_BRIDGE:
|
|
return CommandContainer {
|
|
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,
|
|
nullptr, ""
|
|
};
|
|
case Blueprint::Item::Type::RAIL_STATION_PART:
|
|
return CommandContainer {
|
|
AddTileIndexDiffCWrap(start, item.tdiff),
|
|
(uint32)_cur_railtype
|
|
| ((uint32)item.u.rail.station_part.axis << 6)
|
|
| ((uint32)item.u.rail.station_part.numtracks << 8)
|
|
| ((uint32)item.u.rail.station_part.plat_len << 16),
|
|
(uint32)NEW_STATION << 16,
|
|
CMD_BUILD_RAIL_STATION,
|
|
nullptr, ""
|
|
};
|
|
case Blueprint::Item::Type::RAIL_STATION:
|
|
// TODO station types
|
|
return CommandContainer {
|
|
AddTileIndexDiffCWrap(start, item.tdiff),
|
|
(uint32)_cur_railtype | ((uint32)1 << 8) | ((uint32)1 << 16) | ((uint32)1 << 24),
|
|
(uint32)NEW_STATION << 16,
|
|
CMD_BUILD_RAIL_STATION,
|
|
nullptr, ""
|
|
};
|
|
case Blueprint::Item::Type::RAIL_SIGNAL:
|
|
return CommandContainer {
|
|
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,
|
|
nullptr, ""
|
|
};
|
|
default:
|
|
NOT_REACHED();
|
|
}
|
|
NOT_REACHED();
|
|
}
|
|
|
|
std::multimap<TileIndex, ObjectTileHighlight> Blueprint::GetTiles(TileIndex tile) {
|
|
std::multimap<TileIndex, ObjectTileHighlight> 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);
|
|
};
|
|
|
|
std::set<StationID> can_build_station_sign;
|
|
for (auto &item: this->items) {
|
|
if (item.type != Item::Type::RAIL_STATION) continue;
|
|
if (CanBuild(GetBlueprintCommand(tile, item)))
|
|
can_build_station_sign.insert(item.u.rail.station.id);
|
|
}
|
|
|
|
for (auto &o: this->items) {
|
|
auto otile = AddTileIndexDiffCWrap(tile, o.tdiff);
|
|
auto palette = CM_PALETTE_TINT_WHITE;
|
|
if (o.type != Item::Type::RAIL_SIGNAL && !CanBuild(GetBlueprintCommand(tile, o)))
|
|
palette = CM_PALETTE_TINT_RED_DEEP;
|
|
|
|
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(palette, 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(palette, 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(palette, 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(palette, o.u.rail.tunnel.ddir));
|
|
add_tile(AddTileIndexDiffCWrap(tile, o.u.rail.tunnel.other_end), ObjectTileHighlight::make_rail_tunnel_head(palette, ReverseDiagDir(o.u.rail.tunnel.ddir)));
|
|
break;
|
|
case Item::Type::RAIL_DEPOT:
|
|
add_tile(otile, ObjectTileHighlight::make_rail_depot(palette, o.u.rail.depot.ddir));
|
|
break;
|
|
case Item::Type::RAIL_STATION_PART: {
|
|
auto layout_ptr = AllocaM(byte, (int)o.u.rail.station_part.numtracks * o.u.rail.station_part.plat_len);
|
|
GetStationLayout(layout_ptr, o.u.rail.station_part.numtracks, o.u.rail.station_part.plat_len, nullptr);
|
|
if (palette == CM_PALETTE_TINT_WHITE && can_build_station_sign.find(o.u.rail.station_part.id) == can_build_station_sign.end())
|
|
palette = CM_PALETTE_TINT_ORANGE_DEEP;
|
|
IterateStation(otile, o.u.rail.station_part.axis, o.u.rail.station_part.numtracks, o.u.rail.station_part.plat_len,
|
|
[&](TileIndex tile) {
|
|
byte layout = *layout_ptr++;
|
|
add_tile(tile, ObjectTileHighlight::make_rail_station(palette, o.u.rail.station_part.axis, layout & ~1));
|
|
}
|
|
);
|
|
break;
|
|
}
|
|
case Item::Type::RAIL_SIGNAL:
|
|
add_tile(otile, ObjectTileHighlight::make_rail_signal(CM_PALETTE_TINT_WHITE, 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> 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<Blueprint>();
|
|
|
|
auto rotate = [](TileIndexDiffC td) -> TileIndexDiffC {
|
|
return {static_cast<int16>(td.y), static_cast<int16>(-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: {
|
|
auto ediff = rotate({
|
|
(int16)(o.tdiff.x + (o.u.rail.station_part.axis == AXIS_X ? o.u.rail.station_part.plat_len : o.u.rail.station_part.numtracks) - 1),
|
|
(int16)(o.tdiff.y + (o.u.rail.station_part.axis == AXIS_X ? o.u.rail.station_part.numtracks : o.u.rail.station_part.plat_len) - 1),
|
|
});
|
|
bi.tdiff = {std::min(odiff.x, ediff.x), std::min(odiff.y, ediff.y)};
|
|
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);
|
|
bi.u.rail.station_part.numtracks = o.u.rail.station_part.numtracks;
|
|
bi.u.rail.station_part.plat_len = o.u.rail.station_part.plat_len;
|
|
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->items.push_back(bi);
|
|
}
|
|
res->source_tiles = this->source_tiles;
|
|
return res;
|
|
}
|
|
|
|
|
|
static void BlueprintAddSignals(sp<Blueprint> &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(tile, 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);
|
|
}
|
|
}
|
|
|
|
static void BlueprintAddTracks(sp<Blueprint> &blueprint, TileIndex tile, TileIndexDiffC tdiff, TileArea &area,
|
|
std::map<TileIndex, Track> &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(tile, bi);
|
|
}
|
|
}
|
|
|
|
void BlueprintCopyArea(TileIndex start, TileIndex end) {
|
|
// if (start > end) Swap(start, end);
|
|
TileArea ta{start, end};
|
|
start = ta.tile;
|
|
|
|
ResetActiveBlueprint();
|
|
|
|
auto blueprint = std::make_shared<Blueprint>();
|
|
|
|
std::map<TileIndex, Track> track_tiles;
|
|
std::multimap<StationID, TileIndex> station_tiles;
|
|
std::set<StationID> stations;
|
|
|
|
for (TileIndex tile : ta) {
|
|
TileIndexDiffC td = TileIndexToTileIndexDiffC(tile, start);
|
|
switch (GetTileType(tile)) {
|
|
case MP_STATION:
|
|
if (IsRailStation(tile)) {
|
|
stations.insert(GetStationIndex(tile));
|
|
if (stations.size() > 10) return;
|
|
}
|
|
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(tile, 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(tile, bi);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
if (blueprint->items.size() > 200) return;
|
|
}
|
|
|
|
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<TileIndex> tiles;
|
|
for (TileIndex 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(st->xy, bi);
|
|
|
|
std::set<TileIndex> added_tiles;
|
|
for (auto tile : tiles) {
|
|
if (added_tiles.find(tile) != added_tiles.end())
|
|
continue;
|
|
|
|
auto axis = GetRailStationAxis(tile);
|
|
auto platform_delta = (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
|
|
auto track_delta = (axis == AXIS_Y ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
|
|
auto plat_len = 0;
|
|
|
|
auto can_add_tile = [=](TileIndex tile) {
|
|
return (IsValidTile(tile)
|
|
&& IsTileType(tile, MP_STATION)
|
|
&& GetStationIndex(tile) == sid
|
|
&& IsRailStation(tile)
|
|
&& GetRailStationAxis(tile) == axis);
|
|
};
|
|
|
|
auto cur_tile = tile;
|
|
do {
|
|
plat_len++;
|
|
cur_tile = TILE_ADD(cur_tile, platform_delta);
|
|
} while (can_add_tile(cur_tile));
|
|
|
|
auto numtracks = 0;
|
|
cur_tile = tile;
|
|
bool can_add = false;
|
|
do {
|
|
numtracks++;
|
|
cur_tile = TILE_ADD(cur_tile, track_delta);
|
|
can_add = true;
|
|
auto plat_tile = cur_tile;
|
|
for (auto i = 0; i < plat_len; i++) {
|
|
if (!can_add_tile(plat_tile)) {
|
|
can_add = false;
|
|
break;
|
|
}
|
|
plat_tile = TILE_ADD(plat_tile, platform_delta);
|
|
}
|
|
} while (can_add);
|
|
|
|
cur_tile = tile;
|
|
for (auto i = 0; i < numtracks; i++) {
|
|
auto plat_tile = cur_tile;
|
|
for (auto j = 0; j < plat_len; j++) {
|
|
added_tiles.insert(plat_tile);
|
|
plat_tile = TILE_ADD(plat_tile, platform_delta);
|
|
}
|
|
cur_tile = TILE_ADD(cur_tile, track_delta);
|
|
}
|
|
|
|
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 = axis;
|
|
bi.u.rail.station_part.numtracks = numtracks;
|
|
bi.u.rail.station_part.plat_len = plat_len;
|
|
blueprint->Add(tile, bi);
|
|
}
|
|
}
|
|
|
|
_active_blueprint = std::make_pair(start, blueprint);
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
if (_active_blueprint.second->HasSourceTile(ti->tile)) {
|
|
th.ground_pal = th.structure_pal = CM_PALETTE_TINT_BLUE;
|
|
}
|
|
}
|
|
|
|
void BuildBlueprint(sp<Blueprint> &blueprint, TileIndex start) {
|
|
TileIndex last_tile;
|
|
// uint32 last_p1, last_p2, last_cmd = CMD_END;
|
|
CommandContainer last_rail = {INVALID_TILE, 0, 0, CMD_END};
|
|
for (auto &item : blueprint->items) {
|
|
switch (item.type) {
|
|
case Blueprint::Item::Type::RAIL_TRACK:
|
|
case Blueprint::Item::Type::RAIL_DEPOT:
|
|
case Blueprint::Item::Type::RAIL_TUNNEL:
|
|
case Blueprint::Item::Type::RAIL_BRIDGE: {
|
|
auto cc = GetBlueprintCommand(start, item);
|
|
if (item.type == Blueprint::Item::Type::RAIL_TRACK) last_rail = cc;
|
|
DoCommandP(&cc);
|
|
break;
|
|
}
|
|
case Blueprint::Item::Type::RAIL_STATION: {
|
|
// TODO station types
|
|
TileIndex tile = AddTileIndexDiffCWrap(start, item.tdiff);
|
|
auto cc = GetBlueprintCommand(start, item);
|
|
DoCommandWithCallback(cc,
|
|
[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;
|
|
auto cc = GetBlueprintCommand(start, item);
|
|
DoCommandP(cc.tile, cc.p1 | (1 << 24), station_id << 16, cc.cmd);
|
|
}
|
|
if (!sign_part) DoCommandP(tile, 0, 0, CMD_REMOVE_FROM_RAIL_STATION);
|
|
}
|
|
);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
auto signal_callback = [start, blueprint](bool res) {
|
|
for (auto &item : blueprint->items) {
|
|
if (item.type != Blueprint::Item::Type::RAIL_SIGNAL) continue;
|
|
auto cc = GetBlueprintCommand(start, item);
|
|
DoCommandP(&cc);
|
|
}
|
|
};
|
|
if (last_rail.cmd != CMD_END) { // there can't be any signals if there are no rails
|
|
if (_networking) AddCommandCallback(last_rail.tile, last_rail.p1, last_rail.p2, last_rail.cmd, signal_callback);
|
|
else signal_callback(true);
|
|
}
|
|
}
|
|
|
|
void RotateActiveBlueprint() {
|
|
if (!_active_blueprint.second) return;
|
|
_active_blueprint.second = _active_blueprint.second->Rotate();
|
|
}
|
|
|
|
void BuildActiveBlueprint(TileIndex start) {
|
|
if (!_active_blueprint.second) return;
|
|
BuildBlueprint(_active_blueprint.second, start);
|
|
}
|
|
|
|
|
|
} // namespace citymania
|