Initial version of blueprints (copy/paste)

This commit is contained in:
dP
2020-12-22 02:35:34 +03:00
parent d084a4b8a8
commit feebf5499a
15 changed files with 1009 additions and 39 deletions

View File

@@ -1228,6 +1228,8 @@ citymania/extensions/cmext_company.hpp
# CityMania client # CityMania client
citymania/cm_base64.hpp citymania/cm_base64.hpp
citymania/cm_base64.cpp citymania/cm_base64.cpp
citymania/cm_blueprint.hpp
citymania/cm_blueprint.cpp
citymania/cm_highlight.hpp citymania/cm_highlight.hpp
citymania/cm_highlight.cpp citymania/cm_highlight.cpp
citymania/cm_highlight_type.hpp citymania/cm_highlight_type.hpp

View File

@@ -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 <functional>
#include <map>
extern TileHighlightData _thd;
extern RailType _cur_railtype;
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, 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<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);
};
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> 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 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> &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<Blueprint>();
// _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> &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(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<Blueprint>();
_active_blueprint = std::make_pair(start, blueprint);
std::map<TileIndex, Track> track_tiles;
std::multimap<StationID, TileIndex> station_tiles;
std::set<StationID> 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<TileIndex> 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> &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

View File

@@ -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

View File

@@ -1,10 +1,13 @@
#include "../stdafx.h" #include "../stdafx.h"
#include "cm_highlight.hpp" #include "cm_highlight.hpp"
#include "cm_blueprint.hpp"
#include "cm_main.hpp" #include "cm_main.hpp"
#include "cm_station_gui.hpp" #include "cm_station_gui.hpp"
#include "../core/math_func.hpp" #include "../core/math_func.hpp"
#include "../table/bridge_land.h"
#include "../command_func.h" #include "../command_func.h"
#include "../house.h" #include "../house.h"
#include "../industry.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, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 17, 0, 15, 18, 0,
}; };
ObjectTileHighlight ObjectTileHighlight::make_rail_depot(DiagDirection ddir) {
ObjectTileHighlight ObjectTileHighlight::make_depot(DiagDirection ddir) {
auto oh = ObjectTileHighlight(Type::RAIL_DEPOT); auto oh = ObjectTileHighlight(Type::RAIL_DEPOT);
oh.u.depot.ddir = ddir; oh.u.rail.depot.ddir = ddir;
return oh; return oh;
} }
ObjectTileHighlight ObjectTileHighlight::make_rail(Track track) { ObjectTileHighlight ObjectTileHighlight::make_rail_track(Track track) {
auto oh = ObjectTileHighlight(Type::RAIL_TRACK); auto oh = ObjectTileHighlight(Type::RAIL_TRACK);
oh.u.rail.track = track; oh.u.rail.track = track;
return oh; 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) { bool ObjectHighlight::operator==(const ObjectHighlight& oh) {
if (this->type != oh.type) return false; if (this->type != oh.type) return false;
switch (this->type) { return (this->tile == oh.tile && this->ddir == oh.ddir && this->blueprint == oh.blueprint);
case Type::RAIL_DEPOT: return this->u.depot.tile == oh.u.depot.tile && this->u.depot.ddir == oh.u.depot.ddir; // switch (this->type) {
default: return true; // case Type::RAIL_DEPOT: return this->tile == oh.tile && this->ddir == oh.ddir;
} // default: return true;
return true; // }
// return true;
} }
bool ObjectHighlight::operator!=(const ObjectHighlight& oh) { bool ObjectHighlight::operator!=(const ObjectHighlight& oh) {
@@ -100,9 +129,16 @@ bool ObjectHighlight::operator!=(const ObjectHighlight& oh) {
ObjectHighlight ObjectHighlight::make_depot(TileIndex tile, DiagDirection ddir) { ObjectHighlight ObjectHighlight::make_depot(TileIndex tile, DiagDirection ddir) {
auto oh = ObjectHighlight(ObjectHighlight::Type::RAIL_DEPOT); auto oh = ObjectHighlight{ObjectHighlight::Type::RAIL_DEPOT};
oh.u.depot.tile = tile; oh.tile = tile;
oh.u.depot.ddir = ddir; oh.ddir = ddir;
return oh;
}
ObjectHighlight ObjectHighlight::make_blueprint(TileIndex tile, sp<Blueprint> blueprint) {
auto oh = ObjectHighlight{ObjectHighlight::Type::BLUEPRINT};
oh.tile = tile;
oh.blueprint = blueprint;
return oh; return oh;
} }
@@ -118,7 +154,7 @@ void ObjectHighlight::PlaceExtraDepotRail(TileIndex tile, DiagDirection dir, Tra
if (GetRailTileType(tile) != RAIL_TILE_NORMAL) return; if (GetRailTileType(tile) != RAIL_TILE_NORMAL) return;
if ((GetTrackBits(tile) & DiagdirReachesTracks(dir)) == 0) 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. */ /** Additional pieces of track to add at the entrance of a depot. */
@@ -139,16 +175,20 @@ void ObjectHighlight::UpdateTiles() {
this->tiles.clear(); this->tiles.clear();
switch (this->type) { switch (this->type) {
case Type::RAIL_DEPOT: { case Type::RAIL_DEPOT: {
auto dir = this->u.depot.ddir; auto dir = this->ddir;
this->tiles.insert(std::make_pair(this->u.depot.tile, ObjectTileHighlight::make_depot(dir))); this->tiles.insert(std::make_pair(this->tile, ObjectTileHighlight::make_rail_depot(dir)));
auto tile = this->u.depot.tile + TileOffsByDiagDir(dir); auto tile = this->tile + TileOffsByDiagDir(dir);
if (IsTileType(tile, MP_RAILWAY) && IsCompatibleRail(GetRailType(tile), _cur_railtype)) { if (IsTileType(tile, MP_RAILWAY) && IsCompatibleRail(GetRailType(tile), _cur_railtype)) {
PlaceExtraDepotRail(tile, _place_depot_extra_dir[dir], _place_depot_extra_track[dir]); this->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]); this->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 + 8], _place_depot_extra_track[dir + 8]);
} }
break; break;
} }
case Type::BLUEPRINT:
if (this->blueprint && this->tile != INVALID_TILE)
this->tiles = this->blueprint->GetTiles(this->tile);
break;
default: default:
break; break;
} }
@@ -194,24 +234,208 @@ void DrawTrainDepotSprite(const TileInfo *ti, RailType railtype, DiagDirection d
DrawRailTileSeq(ti, dts, TO_INVALID, offset, 0, PALETTE_TINT_WHITE); 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) { void ObjectHighlight::Draw(const TileInfo *ti) {
this->UpdateTiles(); this->UpdateTiles();
auto range = this->tiles.equal_range(ti->tile); auto range = this->tiles.equal_range(ti->tile);
auto i=0;
for (auto t = range.first; t != range.second; t++) { for (auto t = range.first; t != range.second; t++) {
i++;
auto &oth = t->second; auto &oth = t->second;
switch (oth.type) { switch (oth.type) {
case ObjectTileHighlight::Type::RAIL_DEPOT: case ObjectTileHighlight::Type::RAIL_DEPOT:
DrawTrainDepotSprite(ti, _cur_railtype, oth.u.depot.ddir); DrawTrainDepotSprite(ti, _cur_railtype, oth.u.rail.depot.ddir);
break; break;
case ObjectTileHighlight::Type::RAIL_TRACK: { case ObjectTileHighlight::Type::RAIL_TRACK: {
auto hs = (HighLightStyle)oth.u.rail.track; auto hs = (HighLightStyle)oth.u.rail.track;
DrawAutorailSelection(ti, hs, PAL_NONE); DrawAutorailSelection(ti, hs, PAL_NONE);
break; 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: default:
break; 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); SetStationSelectionHighlight(ti, th);
SetBlueprintHighlight(ti, th);
return th; return th;
} }
@@ -545,6 +770,8 @@ void DrawTileZoning(const TileInfo *ti, const TileHighlight &th) {
bool DrawTileSelection(const TileInfo *ti, const TileHighlightType &tht) { bool DrawTileSelection(const TileInfo *ti, const TileHighlightType &tht) {
_thd.cm.Draw(ti); _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) { if ((_thd.drawstyle & HT_DRAG_MASK) == HT_RECT && _thd.outersize.x > 0) {
// station selector, handled by DrawTileZoning // station selector, handled by DrawTileZoning
return true; return true;
@@ -610,11 +837,14 @@ DiagDirection AutodetectRailObjectDirection(TileIndex tile, Point pt) {
HighLightStyle UpdateTileSelection(HighLightStyle new_drawstyle) { HighLightStyle UpdateTileSelection(HighLightStyle new_drawstyle) {
_thd.cm_new = ObjectHighlight(ObjectHighlight::Type::NONE); _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) { _cursor.sprite_seq[0].sprite == GetRailTypeInfo(_cur_railtype)->cursor.depot) {
auto dir = _build_depot_direction; auto dir = _build_depot_direction;
auto pt = GetTileBelowCursor();
auto tile = TileVirtXY(pt.x, pt.y);
if (pt.x != -1) { if (pt.x != -1) {
if (dir >= DiagDirection::DIAGDIR_END) if (dir >= DiagDirection::DIAGDIR_END)
dir = AutodetectRailObjectDirection(tile, pt); dir = AutodetectRailObjectDirection(tile, pt);

View File

@@ -1,36 +1,152 @@
#ifndef CITYMANIA_HIGHLIGHT_TYPE_HPP #ifndef CITYMANIA_HIGHLIGHT_TYPE_HPP
#define CITYMANIA_HIGHLIGHT_TYPE_HPP #define CITYMANIA_HIGHLIGHT_TYPE_HPP
#include "../bridge.h"
#include "../direction_type.h" #include "../direction_type.h"
#include "../map_func.h"
#include "../signal_type.h"
#include "../station_type.h"
#include "../tile_cmd.h" #include "../tile_cmd.h"
#include "../tile_type.h" #include "../tile_type.h"
#include "../track_type.h" #include "../track_type.h"
#include <map> #include <map>
#include <set>
#include <vector>
namespace citymania { namespace citymania {
class ObjectTileHighlight { class ObjectTileHighlight {
public: public:
enum class Type { enum class Type {
RAIL_DEPOT = 0, BEGIN = 0,
RAIL_TRACK = 1, RAIL_DEPOT = BEGIN,
RAIL_TRACK,
RAIL_STATION,
RAIL_SIGNAL,
RAIL_BRIDGE_HEAD,
RAIL_TUNNEL_HEAD,
END,
}; };
Type type; Type type;
union { union {
struct { struct {
DiagDirection ddir; struct {
} depot; DiagDirection ddir;
struct { } depot;
Track track; 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; } rail;
} u; } u;
ObjectTileHighlight(Type type): type{type} {} ObjectTileHighlight(Type type): type{type} {}
static ObjectTileHighlight make_depot(DiagDirection ddir); static ObjectTileHighlight make_rail_depot(DiagDirection ddir);
static ObjectTileHighlight make_rail(Track track); 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<Item> items;
std::set<TileIndexDiffC, TileIndexDiffCCompare> 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<Blueprint> Rotate();
std::multimap<TileIndex, ObjectTileHighlight> GetTiles(TileIndex tile);
}; };
class ObjectHighlight { class ObjectHighlight {
@@ -38,16 +154,14 @@ public:
enum class Type { enum class Type {
NONE = 0, NONE = 0,
RAIL_DEPOT = 1, RAIL_DEPOT = 1,
BLUEPRINT = 2,
}; };
protected: protected:
Type type; Type type;
union { TileIndex tile = INVALID_TILE;
struct { DiagDirection ddir = INVALID_DIAGDIR;
TileIndex tile; sp<Blueprint> blueprint = nullptr;
DiagDirection ddir;
} depot;
} u;
bool tiles_updated = false; bool tiles_updated = false;
std::multimap<TileIndex, ObjectTileHighlight> tiles; std::multimap<TileIndex, ObjectTileHighlight> tiles;
@@ -55,11 +169,12 @@ protected:
void PlaceExtraDepotRail(TileIndex tile, DiagDirection dir, Track track); void PlaceExtraDepotRail(TileIndex tile, DiagDirection dir, Track track);
public: 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);
bool operator!=(const ObjectHighlight& oh); bool operator!=(const ObjectHighlight& oh);
static ObjectHighlight make_depot(TileIndex tile, DiagDirection ddir); static ObjectHighlight make_depot(TileIndex tile, DiagDirection ddir);
static ObjectHighlight make_blueprint(TileIndex tile, sp<Blueprint> blueprint);
void Draw(const TileInfo *ti); void Draw(const TileInfo *ti);
void MarkDirty(); void MarkDirty();

View File

@@ -32,6 +32,7 @@
#include "table/strings.h" #include "table/strings.h"
#include "citymania/cm_hotkeys.hpp" #include "citymania/cm_hotkeys.hpp"
#include "citymania/cm_blueprint.hpp"
#include "safeguards.h" #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) { if (!estimate_only && !only_sending && callback != nullptr) {
callback(res, tile, p1, p2, cmd); callback(res, tile, p1, p2, cmd);
} }
if (!estimate_only && !only_sending)
citymania::CommandExecuted(res.Succeeded(), tile, p1, p2, cmd);
return res.Succeeded(); return res.Succeeded();
} }

View File

@@ -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 : Show APM counter: {STRING2}
STR_CM_CONFIG_SETTING_SHOW_APM_HELPTEXT : Adds APM (actions per minute) counter to the statusbar. 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_STATUSBAR_APM : {WHITE}APM: {NUM} AVG: {NUM}
STR_CM_RAIL_TOOLBAR_TOOLTIP_BLUEPRINT : {BLACK}Rail blueprint tool (copy-paste)

View File

@@ -38,6 +38,7 @@
#include "widgets/rail_widget.h" #include "widgets/rail_widget.h"
#include "citymania/cm_blueprint.hpp"
#include "citymania/cm_hotkeys.hpp" #include "citymania/cm_hotkeys.hpp"
#include "citymania/cm_station_gui.hpp" #include "citymania/cm_station_gui.hpp"
@@ -768,6 +769,11 @@ struct BuildRailToolbarWindow : Window {
this->last_user_action = widget; this->last_user_action = widget;
break; 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: case WID_RAT_REMOVE:
_remove_button_clicked = citymania::RailToolbar_RemoveModChanged(this, _cm_invert_remove, _remove_button_clicked, true); _remove_button_clicked = citymania::RailToolbar_RemoveModChanged(this, _cm_invert_remove, _remove_button_clicked, true);
// BuildRailClick_Remove(this); // BuildRailClick_Remove(this);
@@ -871,6 +877,15 @@ struct BuildRailToolbarWindow : Window {
PlaceRail_Bridge(tile, this); PlaceRail_Bridge(tile, this);
break; 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: 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); DoCommandP(tile, _cur_railtype | (TRANSPORT_RAIL << 8), 0, CMD_BUILD_TUNNEL | CMD_MSG(STR_ERROR_CAN_T_BUILD_TUNNEL_HERE), CcBuildRailTunnel);
break; break;
@@ -940,6 +955,12 @@ struct BuildRailToolbarWindow : Window {
} }
} }
break; 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); DeleteWindowByClass(WC_BUILD_BRIDGE);
citymania::AbortStationPlacement(); citymania::AbortStationPlacement();
citymania::ResetActiveBlueprint();
} }
void OnPlacePresize(Point pt, TileIndex tile) override void OnPlacePresize(Point pt, TileIndex tile) override
@@ -970,6 +992,10 @@ struct BuildRailToolbarWindow : Window {
EventState CM_OnRemoveModStateChange() override 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); auto new_remove = citymania::RailToolbar_RemoveModChanged(this, _cm_invert_remove, _remove_button_clicked, false);
if (new_remove != _remove_button_clicked) { if (new_remove != _remove_button_clicked) {
_remove_button_clicked = new_remove; _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), 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), 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), 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), 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), 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), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_RAT_CONVERT_RAIL),

View File

@@ -29,6 +29,7 @@ enum HighLightStyle {
HT_VEHICLE = 0x100, ///< vehicle is accepted as target as well (bitmask) 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_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 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. HT_DRAG_MASK = 0x0F8, ///< Mask for the tile drag-type modes.
/* lower bits (used with HT_LINE and HT_RAIL): /* lower bits (used with HT_LINE and HT_RAIL):

View File

@@ -261,6 +261,7 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u
TileIndex tile_start = p1; TileIndex tile_start = p1;
TileIndex tile_end = end_tile; 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 (company == OWNER_DEITY) {
if (transport_type != TRANSPORT_ROAD) return CMD_ERROR; if (transport_type != TRANSPORT_ROAD) return CMD_ERROR;
const Town *town = CalcClosestTownFromTile(tile_start); const Town *town = CalcClosestTownFromTile(tile_start);

View File

@@ -2415,7 +2415,7 @@ bool HandleViewportClicked(const ViewPort *vp, int x, int y, bool double_click)
} }
/* Vehicle placement mode already handled above. */ /* 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) { if (_thd.place_mode & HT_POLY) {
/* In polyline mode double-clicking on a single white line, finishes current polyline. /* 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, * If however the user double-clicks on a line that has a white and a blue section,

View File

@@ -142,6 +142,8 @@ enum ViewportDragDropSelectionProcess {
DDSP_REMOVE_BUSSTOP, ///< Road stop removal (buses) DDSP_REMOVE_BUSSTOP, ///< Road stop removal (buses)
DDSP_REMOVE_TRUCKSTOP, ///< Road stop removal (trucks) DDSP_REMOVE_TRUCKSTOP, ///< Road stop removal (trucks)
DDSP_CONVERT_ROAD, ///< Road conversion DDSP_CONVERT_ROAD, ///< Road conversion
CM_DDSP_BLUEPRINT_AREA, ///< Blueprint area to copy selection
}; };

View File

@@ -27,8 +27,10 @@ enum RailToolbarWidgets {
WID_RAT_BUILD_SIGNALS, ///< Build signals. WID_RAT_BUILD_SIGNALS, ///< Build signals.
WID_RAT_BUILD_BRIDGE, ///< Build a bridge. WID_RAT_BUILD_BRIDGE, ///< Build a bridge.
WID_RAT_BUILD_TUNNEL, ///< Build a tunnel. WID_RAT_BUILD_TUNNEL, ///< Build a tunnel.
CM_WID_RAT_BLUEPRINT, ///< CityMania rail blueprint tool.
WID_RAT_REMOVE, ///< Bulldozer to remove rail. WID_RAT_REMOVE, ///< Bulldozer to remove rail.
WID_RAT_CONVERT_RAIL, ///< Convert other rail to this type. 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. */ /** Widgets of the #BuildRailStationWindow class. */