1443 lines
58 KiB
C++
1443 lines
58 KiB
C++
#include "../stdafx.h"
|
|
|
|
#include "cm_highlight.hpp"
|
|
|
|
#include "cm_blueprint.hpp"
|
|
#include "cm_main.hpp"
|
|
#include "cm_station_gui.hpp"
|
|
#include "cm_zoning.hpp"
|
|
|
|
#include "../core/math_func.hpp"
|
|
#include "../table/bridge_land.h"
|
|
#include "../command_func.h"
|
|
#include "../house.h"
|
|
#include "../industry.h"
|
|
#include "../landscape.h"
|
|
#include "../newgrf_airporttiles.h"
|
|
#include "../newgrf_railtype.h"
|
|
#include "../newgrf_roadtype.h"
|
|
#include "../newgrf_station.h"
|
|
#include "../town.h"
|
|
#include "../town_kdtree.h"
|
|
#include "../tilearea_type.h"
|
|
#include "../tilehighlight_type.h"
|
|
#include "../tilehighlight_func.h"
|
|
#include "../viewport_func.h"
|
|
// #include "../zoning.h"
|
|
#include "../table/airporttile_ids.h"
|
|
#include "../table/track_land.h"
|
|
|
|
#include <set>
|
|
|
|
|
|
/** Enumeration of multi-part foundations */
|
|
enum FoundationPart {
|
|
FOUNDATION_PART_NONE = 0xFF, ///< Neither foundation nor groundsprite drawn yet.
|
|
FOUNDATION_PART_NORMAL = 0, ///< First part (normal foundation or no foundation)
|
|
FOUNDATION_PART_HALFTILE = 1, ///< Second part (halftile foundation)
|
|
FOUNDATION_PART_END
|
|
};
|
|
extern void DrawSelectionSprite(SpriteID image, PaletteID pal, const TileInfo *ti, int z_offset, FoundationPart foundation_part); // viewport.cpp
|
|
extern const Station *_viewport_highlight_station;
|
|
extern TileHighlightData _thd;
|
|
extern bool IsInsideSelectedRectangle(int x, int y);
|
|
extern RailType _cur_railtype;
|
|
extern RoadType _cur_roadtype;
|
|
extern AirportClassID _selected_airport_class; ///< the currently visible airport class
|
|
extern int _selected_airport_index;
|
|
extern byte _selected_airport_layout;
|
|
extern DiagDirection _build_depot_direction; ///< Currently selected depot direction
|
|
extern DiagDirection _road_station_picker_orientation;
|
|
extern DiagDirection _road_depot_orientation;
|
|
extern uint32 _realtime_tick;
|
|
extern void GetStationLayout(byte *layout, uint numtracks, uint plat_len, const StationSpec *statspec);
|
|
|
|
struct RailStationGUISettings {
|
|
Axis orientation; ///< Currently selected rail station orientation
|
|
|
|
bool newstations; ///< Are custom station definitions available?
|
|
StationClassID station_class; ///< Currently selected custom station class (if newstations is \c true )
|
|
byte station_type; ///< %Station type within the currently selected custom station class (if newstations is \c true )
|
|
byte station_count; ///< Number of custom stations (if newstations is \c true )
|
|
};
|
|
extern RailStationGUISettings _railstation; ///< Settings of the station builder GUI
|
|
|
|
namespace citymania {
|
|
|
|
extern void (*DrawTileSelectionRect)(const TileInfo *ti, PaletteID pal);
|
|
extern void (*DrawAutorailSelection)(const TileInfo *ti, HighLightStyle autorail_type, PaletteID pal);
|
|
|
|
struct TileZoning {
|
|
uint8 town_zone : 3;
|
|
uint8 industry_fund_result : 2;
|
|
uint8 advertisement_zone : 2;
|
|
// IndustryType industry_fund_type;
|
|
uint8 industry_fund_update;
|
|
};
|
|
|
|
static TileZoning *_mz = nullptr;
|
|
static IndustryType _industry_forbidden_tiles = INVALID_INDUSTRYTYPE;
|
|
|
|
extern StationBuildingStatus _station_building_status;
|
|
extern const Station *_station_to_join;
|
|
extern const Station *_highlight_station_to_join;
|
|
extern TileArea _highlight_join_area;
|
|
|
|
std::set<std::pair<uint32, const Town*>, std::greater<std::pair<uint32, const Town*>>> _town_cache;
|
|
// struct {
|
|
// int w;
|
|
// int h;
|
|
// int catchment;
|
|
// } _station_select;
|
|
|
|
const byte _tileh_to_sprite[32] = {
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 17, 0, 15, 18, 0,
|
|
};
|
|
|
|
ObjectTileHighlight ObjectTileHighlight::make_rail_depot(SpriteID palette, DiagDirection ddir) {
|
|
auto oh = ObjectTileHighlight(Type::RAIL_DEPOT, palette);
|
|
oh.u.rail.depot.ddir = ddir;
|
|
return oh;
|
|
}
|
|
|
|
ObjectTileHighlight ObjectTileHighlight::make_rail_track(SpriteID palette, Track track) {
|
|
auto oh = ObjectTileHighlight(Type::RAIL_TRACK, palette);
|
|
oh.u.rail.track = track;
|
|
return oh;
|
|
}
|
|
|
|
ObjectTileHighlight ObjectTileHighlight::make_rail_station(SpriteID palette, Axis axis, byte section) {
|
|
auto oh = ObjectTileHighlight(Type::RAIL_STATION, palette);
|
|
oh.u.rail.station.axis = axis;
|
|
oh.u.rail.station.section = section;
|
|
return oh;
|
|
}
|
|
|
|
ObjectTileHighlight ObjectTileHighlight::make_rail_signal(SpriteID palette, uint pos, SignalType type, SignalVariant variant) {
|
|
auto oh = ObjectTileHighlight(Type::RAIL_SIGNAL, palette);
|
|
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(SpriteID palette, DiagDirection ddir, BridgeType type) {
|
|
auto oh = ObjectTileHighlight(Type::RAIL_BRIDGE_HEAD, palette);
|
|
oh.u.rail.bridge_head.ddir = ddir;
|
|
oh.u.rail.bridge_head.type = type;
|
|
return oh;
|
|
}
|
|
|
|
ObjectTileHighlight ObjectTileHighlight::make_rail_tunnel_head(SpriteID palette, DiagDirection ddir) {
|
|
auto oh = ObjectTileHighlight(Type::RAIL_TUNNEL_HEAD, palette);
|
|
oh.u.rail.tunnel_head.ddir = ddir;
|
|
return oh;
|
|
}
|
|
|
|
ObjectTileHighlight ObjectTileHighlight::make_road_stop(SpriteID palette, RoadType roadtype, DiagDirection ddir, bool is_truck) {
|
|
auto oh = ObjectTileHighlight(Type::ROAD_STOP, palette);
|
|
oh.u.road.stop.roadtype = roadtype;
|
|
oh.u.road.stop.ddir = ddir;
|
|
oh.u.road.stop.is_truck = is_truck;
|
|
return oh;
|
|
}
|
|
|
|
ObjectTileHighlight ObjectTileHighlight::make_road_depot(SpriteID palette, RoadType roadtype, DiagDirection ddir) {
|
|
auto oh = ObjectTileHighlight(Type::ROAD_DEPOT, palette);
|
|
oh.u.road.depot.roadtype = roadtype;
|
|
oh.u.road.depot.ddir = ddir;
|
|
return oh;
|
|
}
|
|
|
|
ObjectTileHighlight ObjectTileHighlight::make_airport_tile(SpriteID palette, StationGfx gfx) {
|
|
auto oh = ObjectTileHighlight(Type::AIRPORT_TILE, palette);
|
|
oh.u.airport_tile.gfx = gfx;
|
|
return oh;
|
|
}
|
|
|
|
|
|
bool ObjectHighlight::operator==(const ObjectHighlight& oh) {
|
|
if (this->type != oh.type) return false;
|
|
return (this->tile == oh.tile
|
|
&& this->end_tile == oh.end_tile
|
|
&& this->axis == oh.axis
|
|
&& this->ddir == oh.ddir
|
|
&& this->roadtype == oh.roadtype
|
|
&& this->is_truck == oh.is_truck
|
|
&& this->airport_type == oh.airport_type
|
|
&& this->airport_layout == oh.airport_layout
|
|
&& 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) {
|
|
return !(*this == oh);
|
|
}
|
|
|
|
|
|
ObjectHighlight ObjectHighlight::make_rail_depot(TileIndex tile, DiagDirection ddir) {
|
|
auto oh = ObjectHighlight{ObjectHighlight::Type::RAIL_DEPOT};
|
|
oh.tile = tile;
|
|
oh.ddir = ddir;
|
|
return oh;
|
|
}
|
|
|
|
ObjectHighlight ObjectHighlight::make_rail_station(TileIndex start_tile, TileIndex end_tile, Axis axis) {
|
|
auto oh = ObjectHighlight{ObjectHighlight::Type::RAIL_STATION};
|
|
oh.tile = start_tile;
|
|
oh.end_tile = end_tile;
|
|
oh.axis = axis;
|
|
return oh;
|
|
}
|
|
|
|
ObjectHighlight ObjectHighlight::make_road_stop(TileIndex start_tile, TileIndex end_tile, RoadType roadtype, DiagDirection orientation, bool is_truck) {
|
|
auto oh = ObjectHighlight{ObjectHighlight::Type::ROAD_STOP};
|
|
oh.tile = start_tile;
|
|
oh.end_tile = end_tile;
|
|
oh.ddir = orientation;
|
|
oh.roadtype = roadtype;
|
|
oh.is_truck = is_truck;
|
|
return oh;
|
|
}
|
|
|
|
ObjectHighlight ObjectHighlight::make_road_depot(TileIndex tile, RoadType roadtype, DiagDirection orientation) {
|
|
auto oh = ObjectHighlight{ObjectHighlight::Type::ROAD_DEPOT};
|
|
oh.tile = tile;
|
|
oh.ddir = orientation;
|
|
oh.roadtype = roadtype;
|
|
return oh;
|
|
}
|
|
|
|
ObjectHighlight ObjectHighlight::make_airport(TileIndex start_tile, int airport_type, byte airport_layout) {
|
|
auto oh = ObjectHighlight{ObjectHighlight::Type::AIRPORT};
|
|
oh.tile = start_tile;
|
|
oh.airport_type = airport_type;
|
|
oh.airport_layout = airport_layout;
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Try to add an additional rail-track at the entrance of a depot
|
|
* @param tile Tile to use for adding the rail-track
|
|
* @param dir Direction to check for already present tracks
|
|
* @param track Track to add
|
|
* @see CcRailDepot()
|
|
*/
|
|
void ObjectHighlight::PlaceExtraDepotRail(TileIndex tile, DiagDirection dir, Track track)
|
|
{
|
|
if (GetRailTileType(tile) != RAIL_TILE_NORMAL) return;
|
|
if ((GetTrackBits(tile) & DiagdirReachesTracks(dir)) == 0) return;
|
|
|
|
this->tiles.insert(std::make_pair(tile, ObjectTileHighlight::make_rail_track(CM_PALETTE_TINT_WHITE, track)));
|
|
}
|
|
|
|
/** Additional pieces of track to add at the entrance of a depot. */
|
|
static const Track _place_depot_extra_track[12] = {
|
|
TRACK_LEFT, TRACK_UPPER, TRACK_UPPER, TRACK_RIGHT, // First additional track for directions 0..3
|
|
TRACK_X, TRACK_Y, TRACK_X, TRACK_Y, // Second additional track
|
|
TRACK_LOWER, TRACK_LEFT, TRACK_RIGHT, TRACK_LOWER, // Third additional track
|
|
};
|
|
|
|
/** Direction to check for existing track pieces. */
|
|
static const DiagDirection _place_depot_extra_dir[12] = {
|
|
DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_SE, DIAGDIR_SW,
|
|
DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NE, DIAGDIR_SE,
|
|
DIAGDIR_NW, DIAGDIR_NE, DIAGDIR_NW, DIAGDIR_NE,
|
|
};
|
|
|
|
bool CanBuild(TileIndex tile, uint32 p1, uint32 p2, uint32 cmd) {
|
|
return DoCommandPInternal(
|
|
tile,
|
|
p1,
|
|
p2,
|
|
cmd,
|
|
nullptr, // callback
|
|
"", // text
|
|
true, // my_cmd
|
|
true // estimate_only
|
|
).Succeeded();
|
|
}
|
|
|
|
bool CanBuild(const CommandContainer &cc) {
|
|
return CanBuild(cc.tile, cc.p1, cc.p2, cc.cmd);
|
|
}
|
|
|
|
void ObjectHighlight::UpdateTiles() {
|
|
this->tiles.clear();
|
|
switch (this->type) {
|
|
case Type::NONE:
|
|
break;
|
|
|
|
case Type::RAIL_DEPOT: {
|
|
auto dir = this->ddir;
|
|
|
|
auto palette = (CanBuild(
|
|
this->tile,
|
|
_cur_railtype,
|
|
dir,
|
|
CMD_BUILD_TRAIN_DEPOT
|
|
) ? CM_PALETTE_TINT_WHITE : CM_PALETTE_TINT_RED_DEEP);
|
|
|
|
this->tiles.insert(std::make_pair(this->tile, ObjectTileHighlight::make_rail_depot(palette, dir)));
|
|
auto tile = this->tile + TileOffsByDiagDir(dir);
|
|
if (IsTileType(tile, MP_RAILWAY) && IsCompatibleRail(GetRailType(tile), _cur_railtype)) {
|
|
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::RAIL_STATION: {
|
|
auto ta = OrthogonalTileArea(this->tile, this->end_tile);
|
|
auto numtracks = ta.w;
|
|
auto plat_len = ta.h;
|
|
if (this->axis == AXIS_X) Swap(numtracks, plat_len);
|
|
|
|
auto palette = (CanBuild(
|
|
this->tile,
|
|
_cur_railtype
|
|
| (this->axis << 6)
|
|
| ((uint32)numtracks << 8)
|
|
| ((uint32)plat_len << 16),
|
|
NEW_STATION << 16,
|
|
CMD_BUILD_RAIL_STATION
|
|
) ? CM_PALETTE_TINT_WHITE : CM_PALETTE_TINT_RED_DEEP);
|
|
|
|
auto layout_ptr = AllocaM(byte, (int)numtracks * plat_len);
|
|
GetStationLayout(layout_ptr, numtracks, plat_len, nullptr); // TODO statspec
|
|
|
|
auto tile_delta = (this->axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
|
|
TileIndex tile_track = this->tile;
|
|
do {
|
|
TileIndex tile = tile_track;
|
|
int w = plat_len;
|
|
do {
|
|
byte layout = *layout_ptr++;
|
|
this->tiles.insert(std::make_pair(tile, ObjectTileHighlight::make_rail_station(palette, this->axis, layout & ~1)));
|
|
tile += tile_delta;
|
|
} while (--w);
|
|
tile_track += tile_delta ^ TileDiffXY(1, 1); // perpendicular to tile_delta
|
|
} while (--numtracks);
|
|
|
|
break;
|
|
}
|
|
case Type::ROAD_STOP: {
|
|
auto ta = OrthogonalTileArea(this->tile, this->end_tile);
|
|
auto palette = (CanBuild(
|
|
this->tile,
|
|
(uint32)(ta.w | ta.h << 8),
|
|
(this->is_truck ? 1 : 0) | (this->ddir >= DIAGDIR_END ? 2 : 0) | (((uint)this->ddir % 4) << 3) | (NEW_STATION << 16),
|
|
CMD_BUILD_ROAD_STOP
|
|
) ? CM_PALETTE_TINT_WHITE : CM_PALETTE_TINT_RED_DEEP);
|
|
TileIndex tile;
|
|
for (TileIndex tile : ta) {
|
|
this->tiles.insert(std::make_pair(tile, ObjectTileHighlight::make_road_stop(palette, this->roadtype, this->ddir, this->is_truck)));
|
|
}
|
|
break;
|
|
}
|
|
|
|
case Type::ROAD_DEPOT: {
|
|
auto palette = (CanBuild(
|
|
this->tile,
|
|
this->roadtype << 2 | this->ddir,
|
|
0,
|
|
CMD_BUILD_ROAD_DEPOT
|
|
) ? CM_PALETTE_TINT_WHITE : CM_PALETTE_TINT_RED_DEEP);
|
|
this->tiles.insert(std::make_pair(this->tile, ObjectTileHighlight::make_road_depot(palette, this->roadtype, this->ddir)));
|
|
break;
|
|
}
|
|
|
|
case Type::AIRPORT: {
|
|
auto palette = (CanBuild(
|
|
this->tile,
|
|
this->airport_type | ((uint)this->airport_layout << 8),
|
|
1 | (NEW_STATION << 16),
|
|
CMD_BUILD_AIRPORT
|
|
) ? CM_PALETTE_TINT_WHITE : CM_PALETTE_TINT_RED_DEEP);
|
|
|
|
const AirportSpec *as = AirportSpec::Get(this->airport_type);
|
|
if (!as->IsAvailable() || this->airport_layout >= as->num_table) break;
|
|
Direction rotation = as->rotation[this->airport_layout];
|
|
int w = as->size_x;
|
|
int h = as->size_y;
|
|
if (rotation == DIR_E || rotation == DIR_W) Swap(w, h);
|
|
TileArea airport_area = TileArea(this->tile, w, h);
|
|
for (AirportTileTableIterator iter(as->table[this->airport_layout], this->tile); iter != INVALID_TILE; ++iter) {
|
|
this->tiles.insert(std::make_pair(iter, ObjectTileHighlight::make_airport_tile(palette, iter.GetStationGfx())));
|
|
}
|
|
break;
|
|
}
|
|
case Type::BLUEPRINT:
|
|
if (this->blueprint && this->tile != INVALID_TILE)
|
|
this->tiles = this->blueprint->GetTiles(this->tile);
|
|
break;
|
|
default:
|
|
NOT_REACHED();
|
|
}
|
|
}
|
|
|
|
void ObjectHighlight::MarkDirty() {
|
|
this->UpdateTiles();
|
|
for (const auto &kv: this->tiles) {
|
|
MarkTileDirtyByTile(kv.first);
|
|
}
|
|
if (this->type == ObjectHighlight::Type::BLUEPRINT && this->blueprint) { // TODO why && blueprint check is needed?
|
|
for (auto tile : this->blueprint->source_tiles) {
|
|
MarkTileDirtyByTile(tile);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
SpriteID GetTintBySelectionColour(SpriteID colour, bool deep=false) {
|
|
switch(colour) {
|
|
case CM_SPR_PALETTE_ZONING_RED: return (deep ? CM_PALETTE_TINT_RED_DEEP : CM_PALETTE_TINT_RED);
|
|
case CM_SPR_PALETTE_ZONING_ORANGE: return (deep ? CM_PALETTE_TINT_ORANGE_DEEP : CM_PALETTE_TINT_ORANGE);
|
|
case CM_SPR_PALETTE_ZONING_GREEN: return (deep ? CM_PALETTE_TINT_GREEN_DEEP : CM_PALETTE_TINT_GREEN);
|
|
case CM_SPR_PALETTE_ZONING_LIGHT_BLUE: return (deep ? CM_PALETTE_TINT_CYAN_DEEP : CM_PALETTE_TINT_CYAN);
|
|
case CM_SPR_PALETTE_ZONING_YELLOW: return CM_PALETTE_TINT_YELLOW;
|
|
// case SPR_PALETTE_ZONING__: return PALETTE_TINT_YELLOW_WHITE;
|
|
case CM_SPR_PALETTE_ZONING_WHITE: return CM_PALETTE_TINT_WHITE;
|
|
default: return PAL_NONE;
|
|
}
|
|
}
|
|
|
|
SpriteID GetSelectionColourByTint(SpriteID colour) {
|
|
switch(colour) {
|
|
case CM_PALETTE_TINT_RED_DEEP:
|
|
case CM_PALETTE_TINT_RED:
|
|
return CM_SPR_PALETTE_ZONING_RED;
|
|
case CM_PALETTE_TINT_ORANGE_DEEP:
|
|
case CM_PALETTE_TINT_ORANGE:
|
|
return CM_SPR_PALETTE_ZONING_ORANGE;
|
|
case CM_PALETTE_TINT_GREEN_DEEP:
|
|
case CM_PALETTE_TINT_GREEN:
|
|
return CM_SPR_PALETTE_ZONING_GREEN;
|
|
case CM_PALETTE_TINT_CYAN_DEEP:
|
|
case CM_PALETTE_TINT_CYAN:
|
|
return CM_SPR_PALETTE_ZONING_LIGHT_BLUE;
|
|
case CM_PALETTE_TINT_YELLOW:
|
|
return CM_SPR_PALETTE_ZONING_YELLOW;
|
|
// returnase SPR_PALETTE_ZONING__: return PALETTE_TINT_YELLOW_WHITE;
|
|
case CM_PALETTE_TINT_WHITE:
|
|
return CM_SPR_PALETTE_ZONING_WHITE;
|
|
default: return PAL_NONE;
|
|
}
|
|
}
|
|
|
|
void DrawTrainDepotSprite(SpriteID palette, const TileInfo *ti, RailType railtype, DiagDirection ddir)
|
|
{
|
|
const DrawTileSprites *dts = &_depot_gfx_table[ddir];
|
|
const RailtypeInfo *rti = GetRailTypeInfo(railtype);
|
|
SpriteID image = rti->UsesOverlay() ? SPR_FLAT_GRASS_TILE : dts->ground.sprite;
|
|
uint32 offset = rti->GetRailtypeSpriteOffset();
|
|
|
|
if (image != SPR_FLAT_GRASS_TILE) image += offset;
|
|
// PaletteID palette = COMPANY_SPRITE_COLOUR(_local_company);
|
|
|
|
// DrawSprite(image, PAL_NONE, x, y);
|
|
|
|
switch (ddir) {
|
|
case DIAGDIR_SW: DrawAutorailSelection(ti, HT_DIR_X, GetSelectionColourByTint(palette)); break;
|
|
case DIAGDIR_SE: DrawAutorailSelection(ti, HT_DIR_Y, GetSelectionColourByTint(palette)); break;
|
|
default: break;
|
|
}
|
|
// if (rti->UsesOverlay()) {
|
|
// SpriteID ground = GetCustomRailSprite(rti, INVALID_TILE, RTSG_GROUND);
|
|
|
|
// switch (ddir) {
|
|
// case DIAGDIR_SW: DrawSprite(ground + RTO_X, PALETTE_TINT_WHITE, x, y); break;
|
|
// case DIAGDIR_SE: DrawSprite(ground + RTO_Y, PALETTE_TINT_WHITE, x, y); break;
|
|
// default: break;
|
|
// }
|
|
// }
|
|
int depot_sprite = GetCustomRailSprite(rti, INVALID_TILE, RTSG_DEPOT);
|
|
if (depot_sprite != 0) offset = depot_sprite - SPR_RAIL_DEPOT_SE_1;
|
|
|
|
DrawRailTileSeq(ti, dts, TO_INVALID, offset, 0, palette);
|
|
}
|
|
|
|
void DrawTrainStationSprite(SpriteID palette, const TileInfo *ti, RailType railtype, Axis axis, byte section) {
|
|
int32 total_offset = 0;
|
|
const DrawTileSprites *t = GetStationTileLayout(STATION_RAIL, section + (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), GetSelectionColourByTint(palette));
|
|
|
|
// 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);
|
|
}
|
|
|
|
void DrawRoadStop(SpriteID palette, const TileInfo *ti, RoadType roadtype, DiagDirection orientation, bool is_truck) {
|
|
int32 total_offset = 0;
|
|
const RoadTypeInfo* rti = GetRoadTypeInfo(roadtype);
|
|
|
|
uint image = (uint)orientation;
|
|
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, ti->x, ti->y);
|
|
|
|
SpriteID overlay = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_OVERLAY);
|
|
// if (overlay) DrawSprite(overlay + sprite_offset, PAL_NONE, x, y);
|
|
if (overlay) AddSortableSpriteToDraw(overlay + sprite_offset, palette, ti->x, ti->y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, ti->z);
|
|
} else if (RoadTypeIsTram(roadtype)) {
|
|
// DrawSprite(SPR_TRAMWAY_TRAM + sprite_offset, PAL_NONE, x, y);
|
|
AddSortableSpriteToDraw(SPR_TRAMWAY_TRAM + sprite_offset, palette, ti->x, ti->y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, ti->z);
|
|
}
|
|
} else {
|
|
/* Drive-in stop */
|
|
if (RoadTypeIsRoad(roadtype) && rti->UsesOverlay()) {
|
|
SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_ROADSTOP);
|
|
// DrawSprite(, PAL_NONE, x, y);
|
|
AddSortableSpriteToDraw(ground + image, palette, ti->x, ti->y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, ti->z);
|
|
}
|
|
}
|
|
|
|
const DrawTileSprites *t = GetStationTileLayout(is_truck ? STATION_TRUCK : STATION_BUS, image);
|
|
AddSortableSpriteToDraw(t->ground.sprite, palette, ti->x, ti->y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, ti->z);
|
|
DrawRailTileSeq(ti, t, TO_INVALID, total_offset, 0, palette);
|
|
/* Draw road, tram catenary */
|
|
// DrawRoadCatenary(ti);
|
|
}
|
|
|
|
|
|
struct DrawRoadTileStruct {
|
|
uint16 image;
|
|
byte subcoord_x;
|
|
byte subcoord_y;
|
|
};
|
|
|
|
#include "../table/road_land.h"
|
|
|
|
// copied from road_gui.cpp
|
|
static uint GetRoadSpriteOffset(Slope slope, RoadBits bits)
|
|
{
|
|
if (slope != SLOPE_FLAT) {
|
|
switch (slope) {
|
|
case SLOPE_NE: return 11;
|
|
case SLOPE_SE: return 12;
|
|
case SLOPE_SW: return 13;
|
|
case SLOPE_NW: return 14;
|
|
default: NOT_REACHED();
|
|
}
|
|
} else {
|
|
static const uint offsets[] = {
|
|
0, 18, 17, 7,
|
|
16, 0, 10, 5,
|
|
15, 8, 1, 4,
|
|
9, 3, 6, 2
|
|
};
|
|
return offsets[bits];
|
|
}
|
|
}
|
|
|
|
|
|
void DrawRoadDepot(SpriteID palette, const TileInfo *ti, RoadType roadtype, DiagDirection orientation) {
|
|
const RoadTypeInfo* rti = GetRoadTypeInfo(roadtype);
|
|
int relocation = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_DEPOT);
|
|
bool default_gfx = relocation == 0;
|
|
if (default_gfx) {
|
|
if (HasBit(rti->flags, ROTF_CATENARY)) {
|
|
if (_loaded_newgrf_features.tram == TRAMWAY_REPLACE_DEPOT_WITH_TRACK && RoadTypeIsTram(roadtype) && !rti->UsesOverlay()) {
|
|
/* Sprites with track only work for default tram */
|
|
relocation = SPR_TRAMWAY_DEPOT_WITH_TRACK - SPR_ROAD_DEPOT;
|
|
default_gfx = false;
|
|
} else {
|
|
/* Sprites without track are always better, if provided */
|
|
relocation = SPR_TRAMWAY_DEPOT_NO_TRACK - SPR_ROAD_DEPOT;
|
|
}
|
|
}
|
|
} else {
|
|
relocation -= SPR_ROAD_DEPOT;
|
|
}
|
|
|
|
const DrawTileSprites *dts = &_road_depot[orientation];
|
|
AddSortableSpriteToDraw(dts->ground.sprite, palette, ti->x, ti->y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, ti->z);
|
|
|
|
if (default_gfx) {
|
|
uint offset = GetRoadSpriteOffset(SLOPE_FLAT, DiagDirToRoadBits(orientation));
|
|
if (rti->UsesOverlay()) {
|
|
SpriteID ground = GetCustomRoadSprite(rti, INVALID_TILE, ROTSG_OVERLAY);
|
|
if (ground != 0) AddSortableSpriteToDraw(ground + offset, palette, ti->x, ti->y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, ti->z);
|
|
} else if (RoadTypeIsTram(roadtype)) {
|
|
AddSortableSpriteToDraw(SPR_TRAMWAY_OVERLAY + offset, palette, ti->x, ti->y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, ti->z);
|
|
}
|
|
}
|
|
|
|
DrawRailTileSeq(ti, dts, TO_INVALID, relocation, 0, palette);
|
|
}
|
|
|
|
#include "../table/station_land.h"
|
|
|
|
void DrawAirportTile(SpriteID palette, const TileInfo *ti, StationGfx gfx) {
|
|
int32 total_offset = 0;
|
|
const DrawTileSprites *t = nullptr;
|
|
gfx = GetTranslatedAirportTileID(gfx);
|
|
if (gfx >= NEW_AIRPORTTILE_OFFSET) {
|
|
const AirportTileSpec *ats = AirportTileSpec::Get(gfx);
|
|
if (ats->grf_prop.spritegroup[0] != nullptr /* && DrawNewAirportTile(ti, Station::GetByTile(ti->tile), gfx, ats) */) {
|
|
return;
|
|
}
|
|
/* No sprite group (or no valid one) found, meaning no graphics associated.
|
|
* Use the substitute one instead */
|
|
assert(ats->grf_prop.subst_id != INVALID_AIRPORTTILE);
|
|
gfx = ats->grf_prop.subst_id;
|
|
}
|
|
switch (gfx) {
|
|
case APT_RADAR_GRASS_FENCE_SW:
|
|
t = &_station_display_datas_airport_radar_grass_fence_sw[0];
|
|
break;
|
|
case APT_GRASS_FENCE_NE_FLAG:
|
|
t = &_station_display_datas_airport_flag_grass_fence_ne[0];
|
|
break;
|
|
case APT_RADAR_FENCE_SW:
|
|
t = &_station_display_datas_airport_radar_fence_sw[0];
|
|
break;
|
|
case APT_RADAR_FENCE_NE:
|
|
t = &_station_display_datas_airport_radar_fence_ne[0];
|
|
break;
|
|
case APT_GRASS_FENCE_NE_FLAG_2:
|
|
t = &_station_display_datas_airport_flag_grass_fence_ne_2[0];
|
|
break;
|
|
}
|
|
if (t == nullptr || t->seq == nullptr) t = GetStationTileLayout(STATION_AIRPORT, gfx);
|
|
if (t) {
|
|
AddSortableSpriteToDraw(t->ground.sprite, palette, ti->x, ti->y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, ti->z);
|
|
DrawRailTileSeq(ti, t, TO_INVALID, total_offset, 0, palette);
|
|
}
|
|
}
|
|
|
|
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(SpriteID palette, 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, 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(SpriteID palette, 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, 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(SpriteID palette, 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, ti->x, ti->y, 16, 16, 0, ti->z);
|
|
}
|
|
|
|
void ObjectHighlight::Draw(const TileInfo *ti) {
|
|
auto range = this->tiles.equal_range(ti->tile);
|
|
for (auto t = range.first; t != range.second; t++) {
|
|
auto &oth = t->second;
|
|
switch (oth.type) {
|
|
case ObjectTileHighlight::Type::RAIL_DEPOT:
|
|
DrawTrainDepotSprite(oth.palette, ti, _cur_railtype, oth.u.rail.depot.ddir);
|
|
break;
|
|
case ObjectTileHighlight::Type::RAIL_TRACK: {
|
|
auto hs = (HighLightStyle)oth.u.rail.track;
|
|
DrawAutorailSelection(ti, hs, GetSelectionColourByTint(oth.palette));
|
|
break;
|
|
}
|
|
case ObjectTileHighlight::Type::RAIL_STATION:
|
|
DrawTrainStationSprite(oth.palette, ti, _cur_railtype, oth.u.rail.station.axis, oth.u.rail.station.section);
|
|
break;
|
|
case ObjectTileHighlight::Type::RAIL_SIGNAL:
|
|
DrawSignal(oth.palette, ti, _cur_railtype, oth.u.rail.signal.pos, oth.u.rail.signal.type, oth.u.rail.signal.variant);
|
|
break;
|
|
case ObjectTileHighlight::Type::RAIL_BRIDGE_HEAD:
|
|
DrawBridgeHead(oth.palette, ti, _cur_railtype, oth.u.rail.bridge_head.ddir, oth.u.rail.bridge_head.type);
|
|
break;
|
|
case ObjectTileHighlight::Type::RAIL_TUNNEL_HEAD:
|
|
DrawTunnelHead(oth.palette, ti, _cur_railtype, oth.u.rail.tunnel_head.ddir);
|
|
break;
|
|
case ObjectTileHighlight::Type::ROAD_STOP:
|
|
DrawRoadStop(oth.palette, ti, oth.u.road.stop.roadtype, oth.u.road.stop.ddir, oth.u.road.stop.is_truck);
|
|
break;
|
|
case ObjectTileHighlight::Type::ROAD_DEPOT:
|
|
DrawRoadDepot(oth.palette, ti, oth.u.road.depot.roadtype, oth.u.road.depot.ddir);
|
|
break;
|
|
case ObjectTileHighlight::Type::AIRPORT_TILE:
|
|
DrawAirportTile(oth.palette, ti, oth.u.airport_tile.gfx);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
// fprintf(stderr, "TILEH DRAW %d %d %d\n", ti->tile, (int)i, (int)this->tiles.size());
|
|
}
|
|
|
|
|
|
template <typename F>
|
|
uint8 Get(uint32 x, uint32 y, F getter) {
|
|
if (x >= MapSizeX() || y >= MapSizeY()) return 0;
|
|
return getter(TileXY(x, y));
|
|
}
|
|
|
|
template <typename F>
|
|
std::pair<ZoningBorder, uint8> CalcTileBorders(TileIndex tile, F getter) {
|
|
auto x = TileX(tile), y = TileY(tile);
|
|
ZoningBorder res = ZoningBorder::NONE;
|
|
auto z = getter(tile);
|
|
if (z == 0)
|
|
return std::make_pair(res, 0);
|
|
auto tr = Get(x - 1, y, getter);
|
|
auto tl = Get(x, y - 1, getter);
|
|
auto bl = Get(x + 1, y, getter);
|
|
auto br = Get(x, y + 1, getter);
|
|
if (tr < z) res |= ZoningBorder::TOP_RIGHT;
|
|
if (tl < z) res |= ZoningBorder::TOP_LEFT;
|
|
if (bl < z) res |= ZoningBorder::BOTTOM_LEFT;
|
|
if (br < z) res |= ZoningBorder::BOTTOM_RIGHT;
|
|
if (tr == z && tl == z && Get(x - 1, y - 1, getter) < z) res |= ZoningBorder::TOP_CORNER;
|
|
if (tr == z && br == z && Get(x - 1, y + 1, getter) < z) res |= ZoningBorder::RIGHT_CORNER;
|
|
if (br == z && bl == z && Get(x + 1, y + 1, getter) < z) res |= ZoningBorder::BOTTOM_CORNER;
|
|
if (tl == z && bl == z && Get(x + 1, y - 1, getter) < z) res |= ZoningBorder::LEFT_CORNER;
|
|
return std::make_pair(res, z);
|
|
}
|
|
|
|
static uint8 _industry_highlight_hash = 0;
|
|
|
|
void UpdateIndustryHighlight() {
|
|
_industry_highlight_hash++;
|
|
}
|
|
|
|
bool CanBuildIndustryOnTileCached(IndustryType type, TileIndex tile) {
|
|
// if (_mz[tile].industry_fund_type != type || !_mz[tile].industry_fund_result) {
|
|
if (_mz[tile].industry_fund_update != _industry_highlight_hash || !_mz[tile].industry_fund_result) {
|
|
bool res = CanBuildIndustryOnTile(type, tile);
|
|
// _mz[tile].industry_fund_type = type;
|
|
_mz[tile].industry_fund_update = _industry_highlight_hash;
|
|
_mz[tile].industry_fund_result = res ? 2 : 1;
|
|
return res;
|
|
}
|
|
return (_mz[tile].industry_fund_result == 2);
|
|
}
|
|
|
|
void DrawBorderSprites(const TileInfo *ti, ZoningBorder border, SpriteID color) {
|
|
auto b = (uint8)border & 15;
|
|
auto tile_sprite = CM_SPR_BORDER_HIGHLIGHT_BASE + _tileh_to_sprite[ti->tileh] * 19;
|
|
if (b) {
|
|
DrawSelectionSprite(tile_sprite + b - 1, color, ti, 7, FOUNDATION_PART_NORMAL);
|
|
}
|
|
if (border & ZoningBorder::TOP_CORNER)
|
|
DrawSelectionSprite(tile_sprite + 15, color, ti, 7, FOUNDATION_PART_NORMAL);
|
|
if (border & ZoningBorder::RIGHT_CORNER)
|
|
DrawSelectionSprite(tile_sprite + 16, color, ti, 7, FOUNDATION_PART_NORMAL);
|
|
if (border & ZoningBorder::BOTTOM_CORNER)
|
|
DrawSelectionSprite(tile_sprite + 17, color, ti, 7, FOUNDATION_PART_NORMAL);
|
|
if (border & ZoningBorder::LEFT_CORNER)
|
|
DrawSelectionSprite(tile_sprite + 18, color, ti, 7, FOUNDATION_PART_NORMAL);
|
|
}
|
|
|
|
SpriteID GetIndustryZoningPalette(TileIndex tile) {
|
|
if (!IsTileType(tile, MP_INDUSTRY)) return PAL_NONE;
|
|
Industry *ind = Industry::GetByTile(tile);
|
|
auto n_produced = 0;
|
|
auto n_serviced = 0;
|
|
for (auto j = 0; j < INDUSTRY_NUM_OUTPUTS; j++) {
|
|
if (ind->produced_cargo[j] == CT_INVALID) continue;
|
|
if (ind->last_month_production[j] == 0 && ind->this_month_production[j] == 0) continue;
|
|
n_produced++;
|
|
if (ind->last_month_transported[j] > 0 || ind->last_month_transported[j] > 0)
|
|
n_serviced++;
|
|
}
|
|
if (n_serviced < n_produced)
|
|
return (n_serviced == 0 ? CM_PALETTE_TINT_RED_DEEP : CM_PALETTE_TINT_ORANGE_DEEP);
|
|
return PAL_NONE;
|
|
}
|
|
|
|
static void SetStationSelectionHighlight(const TileInfo *ti, TileHighlight &th) {
|
|
bool draw_selection = ((_thd.drawstyle & HT_DRAG_MASK) == HT_RECT && _thd.outersize.x > 0);
|
|
const Station *highlight_station = _viewport_highlight_station;
|
|
|
|
if (_highlight_station_to_join) highlight_station = _highlight_station_to_join;
|
|
|
|
if (draw_selection) {
|
|
// const SpriteID pal[] = {SPR_PALETTE_ZONING_RED, SPR_PALETTE_ZONING_YELLOW, SPR_PALETTE_ZONING_LIGHT_BLUE, SPR_PALETTE_ZONING_GREEN};
|
|
// auto color = pal[(int)_station_building_status];
|
|
// if (_thd.make_square_red) color = SPR_PALETTE_ZONING_RED;
|
|
if (_thd.make_square_red) {
|
|
auto b = CalcTileBorders(ti->tile, [](TileIndex t) {
|
|
auto x = TileX(t) * TILE_SIZE, y = TileY(t) * TILE_SIZE;
|
|
return IsInsideSelectedRectangle(x, y);
|
|
});
|
|
if (b.first != ZoningBorder::NONE)
|
|
th.add_border(b.first, CM_SPR_PALETTE_ZONING_RED);
|
|
}
|
|
if (IsInsideSelectedRectangle(TileX(ti->tile) * TILE_SIZE, TileY(ti->tile) * TILE_SIZE)) {
|
|
// th.ground_pal = GetTintBySelectionColour(color);
|
|
th.ground_pal = th.structure_pal = (_thd.make_square_red ? CM_PALETTE_TINT_RED : PAL_NONE);
|
|
return;
|
|
}
|
|
}
|
|
|
|
auto coverage_getter = [draw_selection, highlight_station](TileIndex t) {
|
|
auto x = TileX(t) * TILE_SIZE, y = TileY(t) * TILE_SIZE;
|
|
if (highlight_station && IsTileType(t, MP_STATION) && GetStationIndex(t) == highlight_station->index) return 2;
|
|
if (_settings_client.gui.station_show_coverage && highlight_station && highlight_station->TileIsInCatchment(t)) return 1;
|
|
if (draw_selection && _settings_client.gui.station_show_coverage && IsInsideBS(x, _thd.pos.x + _thd.offs.x, _thd.size.x + _thd.outersize.x) &&
|
|
IsInsideBS(y, _thd.pos.y + _thd.offs.y, _thd.size.y + _thd.outersize.y)) return 1;
|
|
return 0;
|
|
};
|
|
auto b = CalcTileBorders(ti->tile, coverage_getter);
|
|
if (b.second) {
|
|
// const SpriteID pal[] = {PAL_NONE, SPR_PALETTE_ZONING_WHITE, SPR_PALETTE_ZONING_LIGHT_BLUE};
|
|
const SpriteID pal[] = {PAL_NONE, CM_SPR_PALETTE_ZONING_WHITE, PAL_NONE};
|
|
th.add_border(b.first, pal[b.second]);
|
|
const SpriteID pal2[] = {PAL_NONE, CM_PALETTE_TINT_WHITE, CM_PALETTE_TINT_BLUE};
|
|
th.ground_pal = th.structure_pal = pal2[b.second];
|
|
}
|
|
|
|
if (_highlight_join_area.tile != INVALID_TILE) {
|
|
auto b = CalcTileBorders(ti->tile, [](TileIndex t) {
|
|
return _highlight_join_area.Contains(t) ? 1 : 0;
|
|
});
|
|
th.add_border(b.first, CM_SPR_PALETTE_ZONING_LIGHT_BLUE);
|
|
if (b.second) {
|
|
switch (th.ground_pal) {
|
|
case CM_PALETTE_TINT_WHITE: th.ground_pal = th.structure_pal = CM_PALETTE_TINT_CYAN_WHITE; break;
|
|
case CM_PALETTE_TINT_BLUE: break;
|
|
default: th.ground_pal = th.structure_pal = CM_PALETTE_TINT_CYAN; break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CalcCBAcceptanceBorders(TileHighlight &th, TileIndex tile, SpriteID border_pal, SpriteID ground_pal) {
|
|
int tx = TileX(tile), ty = TileY(tile);
|
|
uint16 radius = _settings_client.gui.cm_cb_distance;
|
|
bool in_zone = false;
|
|
ZoningBorder border = ZoningBorder::NONE;
|
|
_town_kdtree.FindContained(
|
|
(uint16)std::max<int>(0, tx - radius),
|
|
(uint16)std::max<int>(0, ty - radius),
|
|
(uint16)std::min<int>(tx + radius + 1, MapSizeX()),
|
|
(uint16)std::min<int>(ty + radius + 1, MapSizeY()),
|
|
[tx, ty, radius, &in_zone, &border] (TownID tid) {
|
|
Town *town = Town::GetIfValid(tid);
|
|
if (!town || town->larger_town)
|
|
return;
|
|
|
|
int dx = TileX(town->xy) - tx;
|
|
int dy = TileY(town->xy) - ty;
|
|
in_zone = in_zone || (std::max(abs(dx), abs(dy)) <= radius);
|
|
if (dx == radius) border |= ZoningBorder::TOP_RIGHT;
|
|
if (dx == -radius) border |= ZoningBorder::BOTTOM_LEFT;
|
|
if (dy == radius) border |= ZoningBorder::TOP_LEFT;
|
|
if (dy == -radius) border |= ZoningBorder::BOTTOM_RIGHT;
|
|
}
|
|
);
|
|
th.add_border(border, border_pal);
|
|
if (in_zone) th.tint_all(ground_pal);
|
|
}
|
|
|
|
|
|
void AddTownCBLimitBorder(TileIndex tile, const Town *town, ZoningBorder &border, bool &in_zone) {
|
|
auto sq = town->cache.squared_town_zone_radius[0] + 30;
|
|
auto x = CalcTileBorders(tile, [town, sq] (TileIndex tile) {
|
|
return DistanceSquare(tile, town->xy) <= sq ? 1 : 0;
|
|
});
|
|
border |= x.first;
|
|
in_zone = in_zone || x.second;
|
|
}
|
|
|
|
void CalcCBTownLimitBorder(TileHighlight &th, TileIndex tile, SpriteID border_pal, SpriteID ground_pal) {
|
|
auto n = Town::GetNumItems();
|
|
uint32 sq = 0;
|
|
uint i = 0;
|
|
ZoningBorder border = ZoningBorder::NONE;
|
|
bool in_zone = false;
|
|
for (auto &p : _town_cache) {
|
|
sq = p.second->cache.squared_town_zone_radius[0] + 30;
|
|
if (4 * sq * n < MapSize() * i) break;
|
|
AddTownCBLimitBorder(tile, p.second, border, in_zone);
|
|
i++;
|
|
}
|
|
uint radius = IntSqrt(sq);
|
|
int tx = TileX(tile), ty = TileY(tile);
|
|
_town_kdtree.FindContained(
|
|
(uint16)std::max<int>(0, tx - radius),
|
|
(uint16)std::max<int>(0, ty - radius),
|
|
(uint16)std::min<int>(tx + radius + 1, MapSizeX()),
|
|
(uint16)std::min<int>(ty + radius + 1, MapSizeY()),
|
|
[tile, &in_zone, &border] (TownID tid) {
|
|
Town *town = Town::GetIfValid(tid);
|
|
if (!town || town->larger_town)
|
|
return;
|
|
AddTownCBLimitBorder(tile, town, border, in_zone);
|
|
}
|
|
);
|
|
th.add_border(border, border_pal);
|
|
if (in_zone) th.tint_all(ground_pal);
|
|
}
|
|
|
|
TileHighlight GetTileHighlight(const TileInfo *ti, TileType tile_type) {
|
|
TileHighlight th;
|
|
if (ti->tile == INVALID_TILE || tile_type == MP_VOID) return th;
|
|
if (_zoning.outer == CHECKTOWNZONES) {
|
|
auto p = GetTownZoneBorder(ti->tile);
|
|
auto color = PAL_NONE;
|
|
switch (p.second) {
|
|
default: break; // Tz0
|
|
case 1: color = CM_SPR_PALETTE_ZONING_WHITE; break; // Tz0
|
|
case 2: color = CM_SPR_PALETTE_ZONING_YELLOW; break; // Tz1
|
|
case 3: color = CM_SPR_PALETTE_ZONING_ORANGE; break; // Tz2
|
|
case 4: color = CM_SPR_PALETTE_ZONING_ORANGE; break; // Tz3
|
|
case 5: color = CM_SPR_PALETTE_ZONING_RED; break; // Tz4 - center
|
|
};
|
|
th.add_border(p.first, color);
|
|
th.ground_pal = th.structure_pal = GetTintBySelectionColour(color);
|
|
if (CB_Enabled())
|
|
CalcCBTownLimitBorder(th, ti->tile, CM_SPR_PALETTE_ZONING_RED, PAL_NONE);
|
|
} else if (_zoning.outer == CHECKSTACATCH) {
|
|
th.add_border(citymania::GetAnyStationCatchmentBorder(ti->tile),
|
|
CM_SPR_PALETTE_ZONING_LIGHT_BLUE);
|
|
} else if (_zoning.outer == CHECKTOWNGROWTHTILES) {
|
|
// if (tgt == TGTS_NEW_HOUSE) th.sprite = SPR_IMG_HOUSE_NEW;
|
|
switch (_game->get_town_growth_tile(ti->tile)) {
|
|
// case TGTS_CB_HOUSE_REMOVED_NOGROW:
|
|
case TownGrowthTileState::RH_REMOVED:
|
|
th.selection = CM_SPR_PALETTE_ZONING_LIGHT_BLUE;
|
|
break;
|
|
case TownGrowthTileState::RH_REBUILT:
|
|
th.selection = CM_SPR_PALETTE_ZONING_WHITE;
|
|
th.structure_pal = CM_PALETTE_TINT_WHITE;
|
|
break;
|
|
case TownGrowthTileState::NEW_HOUSE:
|
|
th.selection = CM_SPR_PALETTE_ZONING_GREEN;
|
|
th.structure_pal = CM_PALETTE_TINT_GREEN;
|
|
break;
|
|
case TownGrowthTileState::CS:
|
|
th.selection = CM_SPR_PALETTE_ZONING_ORANGE;
|
|
break;
|
|
case TownGrowthTileState::HS:
|
|
th.selection = CM_SPR_PALETTE_ZONING_YELLOW;
|
|
break;
|
|
case TownGrowthTileState::HR:
|
|
th.selection = CM_SPR_PALETTE_ZONING_RED;
|
|
break;
|
|
default: th.selection = PAL_NONE;
|
|
}
|
|
if (th.selection) th.ground_pal = GetTintBySelectionColour(th.selection);
|
|
} else if (_zoning.outer == CHECKBULUNSER) {
|
|
if (IsTileType (ti->tile, MP_HOUSE)) {
|
|
StationFinder stations(TileArea(ti->tile, 1, 1));
|
|
|
|
// TODO check cargos
|
|
if (stations.GetStations()->empty())
|
|
th.ground_pal = th.structure_pal = CM_PALETTE_TINT_RED_DEEP;
|
|
}
|
|
} else if (_zoning.outer == CHECKINDUNSER) {
|
|
auto pal = GetIndustryZoningPalette(ti->tile);
|
|
if (pal) th.ground_pal = th.structure_pal = CM_PALETTE_TINT_RED_DEEP;
|
|
} else if (_zoning.outer == CHECKTOWNADZONES) {
|
|
auto getter = [](TileIndex t) { return _mz[t].advertisement_zone; };
|
|
auto b = CalcTileBorders(ti->tile, getter);
|
|
const SpriteID pal[] = {PAL_NONE, CM_SPR_PALETTE_ZONING_YELLOW, CM_SPR_PALETTE_ZONING_ORANGE, CM_SPR_PALETTE_ZONING_RED};
|
|
th.add_border(b.first, pal[b.second]);
|
|
auto check_tile = ti->tile;
|
|
if (IsTileType (ti->tile, MP_STATION)) {
|
|
auto station = Station::GetByTile(ti->tile);
|
|
if (station) check_tile = station->xy;
|
|
}
|
|
auto z = getter(check_tile);
|
|
if (z) th.ground_pal = th.structure_pal = GetTintBySelectionColour(pal[z]);
|
|
} else if (_zoning.outer == CHECKCBACCEPTANCE) {
|
|
CalcCBAcceptanceBorders(th, ti->tile, CM_SPR_PALETTE_ZONING_WHITE, CM_PALETTE_TINT_WHITE);
|
|
} else if (_zoning.outer == CHECKCBTOWNLIMIT) {
|
|
CalcCBTownLimitBorder(th, ti->tile, CM_SPR_PALETTE_ZONING_WHITE, CM_PALETTE_TINT_WHITE);
|
|
} else if (_zoning.outer == CHECKACTIVESTATIONS) {
|
|
auto getter = [](TileIndex t) {
|
|
if (!IsTileType (t, MP_STATION)) return 0;
|
|
Station *st = Station::GetByTile(t);
|
|
if (!st) return 0;
|
|
if (st->time_since_load <= 20 || st->time_since_unload <= 20)
|
|
return 1;
|
|
return 2;
|
|
};
|
|
auto b = CalcTileBorders(ti->tile, getter);
|
|
const SpriteID pal[] = {PAL_NONE, CM_SPR_PALETTE_ZONING_GREEN, CM_SPR_PALETTE_ZONING_RED};
|
|
th.add_border(b.first, pal[b.second]);
|
|
auto z = getter(ti->tile);
|
|
if (z) th.ground_pal = th.structure_pal = GetTintBySelectionColour(pal[z]);
|
|
}
|
|
|
|
if (_settings_client.gui.cm_show_industry_forbidden_tiles &&
|
|
_industry_forbidden_tiles != INVALID_INDUSTRYTYPE) {
|
|
auto b = CalcTileBorders(ti->tile, [](TileIndex t) { return !CanBuildIndustryOnTileCached(_industry_forbidden_tiles, t); });
|
|
th.add_border(b.first, CM_SPR_PALETTE_ZONING_RED);
|
|
if (!CanBuildIndustryOnTileCached(_industry_forbidden_tiles, ti->tile))
|
|
th.ground_pal = th.structure_pal = CM_PALETTE_TINT_RED;
|
|
}
|
|
|
|
SetStationSelectionHighlight(ti, th);
|
|
SetBlueprintHighlight(ti, th);
|
|
|
|
return th;
|
|
}
|
|
|
|
void DrawTileZoning(const TileInfo *ti, const TileHighlight &th, TileType tile_type) {
|
|
if (ti->tile == INVALID_TILE || tile_type == MP_VOID) return;
|
|
for (uint i = 0; i < th.border_count; i++)
|
|
DrawBorderSprites(ti, th.border[i], th.border_color[i]);
|
|
if (th.sprite) {
|
|
DrawSelectionSprite(th.sprite, PAL_NONE, ti, 0, FOUNDATION_PART_NORMAL);
|
|
}
|
|
if (th.selection) {
|
|
DrawBorderSprites(ti, ZoningBorder::FULL, th.selection);
|
|
// DrawSelectionSprite(SPR_SELECT_TILE + _tileh_to_sprite[ti->tileh],
|
|
// th.selection, ti, 0, FOUNDATION_PART_NORMAL);
|
|
}
|
|
}
|
|
|
|
bool DrawTileSelection(const TileInfo *ti, const TileHighlightType &tht) {
|
|
if (ti->tile == INVALID_TILE || IsTileType(ti->tile, MP_VOID)) return false;
|
|
_thd.cm.Draw(ti);
|
|
|
|
if (_thd.drawstyle == CM_HT_BLUEPRINT_PLACE) return true;
|
|
|
|
if (_thd.select_proc == DDSP_BUILD_STATION || _thd.select_proc == DDSP_BUILD_BUSSTOP
|
|
|| _thd.select_proc == DDSP_BUILD_TRUCKSTOP || _thd.select_proc == CM_DDSP_BUILD_AIRPORT
|
|
|| _thd.select_proc == CM_DDSP_BUILD_ROAD_DEPOT || _thd.select_proc == CM_DDSP_BUILD_RAIL_DEPOT) {
|
|
// handled by DrawTileZoning
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
TileIndex _autodetection_tile = INVALID_TILE;
|
|
DiagDirDiff _autodetection_rotation = DIAGDIRDIFF_SAME;
|
|
|
|
static DiagDirDiff GetAutodetectionRotation() {
|
|
auto pt = GetTileBelowCursor();
|
|
auto tile = TileVirtXY(pt.x, pt.y);
|
|
|
|
if (tile != _autodetection_tile) {
|
|
_autodetection_tile = tile;
|
|
_autodetection_rotation = DIAGDIRDIFF_SAME;
|
|
}
|
|
|
|
return _autodetection_rotation;
|
|
}
|
|
|
|
void RotateAutodetection() {
|
|
auto rotation = GetAutodetectionRotation();
|
|
if (rotation == DIAGDIRDIFF_90LEFT) rotation = DIAGDIRDIFF_SAME;
|
|
else rotation++;
|
|
_autodetection_rotation = rotation;
|
|
::UpdateTileSelection();
|
|
}
|
|
|
|
void ResetRotateAutodetection() {
|
|
_autodetection_tile = INVALID_TILE;
|
|
_autodetection_rotation = DIAGDIRDIFF_SAME;
|
|
}
|
|
|
|
DiagDirection AddAutodetectionRotation(DiagDirection ddir) {
|
|
if (ddir >= DIAGDIR_END) return (DiagDirection)(((uint)ddir + (uint)GetAutodetectionRotation()) % 2 + DIAGDIR_END);
|
|
return ChangeDiagDir(ddir, GetAutodetectionRotation());
|
|
}
|
|
|
|
HighLightStyle UpdateTileSelection(HighLightStyle new_drawstyle) {
|
|
_thd.cm_new = ObjectHighlight(ObjectHighlight::Type::NONE);
|
|
auto pt = GetTileBelowCursor();
|
|
auto tile = (pt.x == -1 ? INVALID_TILE : TileVirtXY(pt.x, pt.y));
|
|
// fprintf(stderr, "UPDATE %d %d %d %d\n", tile, _thd.size.x, _thd.size.y, (int)((_thd.place_mode & HT_DRAG_MASK) == HT_RECT));
|
|
if (_thd.place_mode == CM_HT_BLUEPRINT_PLACE) {
|
|
UpdateBlueprintTileSelection(pt, tile);
|
|
new_drawstyle = CM_HT_BLUEPRINT_PLACE;
|
|
} else if (pt.x == -1) {
|
|
} else if (_thd.make_square_red) {
|
|
} else if (_thd.select_proc == CM_DDSP_BUILD_ROAD_DEPOT) {
|
|
auto dir = _road_depot_orientation;
|
|
if (dir == DEPOTDIR_AUTO) {
|
|
dir = AddAutodetectionRotation(AutodetectRoadObjectDirection(tile, pt, _cur_roadtype));
|
|
}
|
|
_thd.cm_new = ObjectHighlight::make_road_depot(tile, _cur_roadtype, dir);
|
|
new_drawstyle = HT_RECT;
|
|
} else if (_thd.select_proc == CM_DDSP_BUILD_RAIL_DEPOT) {
|
|
auto dir = _build_depot_direction;
|
|
if (dir >= DiagDirection::DIAGDIR_END) {
|
|
dir = AddAutodetectionRotation(AutodetectRailObjectDirection(tile, pt));
|
|
}
|
|
_thd.cm_new = ObjectHighlight::make_rail_depot(tile, dir);
|
|
// } else if (((_thd.place_mode & HT_DRAG_MASK) == HT_RECT || ((_thd.place_mode & HT_DRAG_MASK) == HT_SPECIAL && (_thd.next_drawstyle & HT_DRAG_MASK) == HT_RECT)) && _thd.new_outersize.x > 0 && !_thd.make_square_red) { // station
|
|
} else if (_thd.select_proc == CM_DDSP_BUILD_AIRPORT) {
|
|
auto tile = TileXY(_thd.new_pos.x / TILE_SIZE, _thd.new_pos.y / TILE_SIZE);
|
|
if (_selected_airport_index != -1) {
|
|
const AirportSpec *as = AirportClass::Get(_selected_airport_class)->GetSpec(_selected_airport_index);
|
|
_thd.cm_new = ObjectHighlight::make_airport(tile, as->GetIndex(), _selected_airport_layout);
|
|
new_drawstyle = HT_RECT;
|
|
}
|
|
} else if (_thd.select_proc == DDSP_BUILD_STATION || _thd.select_proc == DDSP_BUILD_BUSSTOP
|
|
|| _thd.select_proc == DDSP_BUILD_TRUCKSTOP) { // station
|
|
if (_thd.size.x >= (int)TILE_SIZE && _thd.size.y >= (int)TILE_SIZE) {
|
|
auto start_tile = TileXY(_thd.new_pos.x / TILE_SIZE, _thd.new_pos.y / TILE_SIZE);
|
|
auto end_tile = TileXY(
|
|
std::min((_thd.new_pos.x + _thd.new_size.x) / TILE_SIZE, MapSizeX()) - 1,
|
|
std::min((_thd.new_pos.y + _thd.new_size.y) / TILE_SIZE, MapSizeY()) - 1
|
|
);
|
|
if (_thd.select_proc == DDSP_BUILD_STATION)
|
|
_thd.cm_new = ObjectHighlight::make_rail_station(start_tile, end_tile, _railstation.orientation);
|
|
else if (_thd.select_proc == DDSP_BUILD_BUSSTOP || _thd.select_proc == DDSP_BUILD_TRUCKSTOP) {
|
|
auto ddir = _road_station_picker_orientation;
|
|
auto ta = TileArea(start_tile, end_tile);
|
|
if (pt.x != -1) {
|
|
if (ddir >= DIAGDIR_END && ddir < STATIONDIR_AUTO) {
|
|
// When placed on road autorotate anyway
|
|
if (ddir == STATIONDIR_X) {
|
|
if (!CheckDriveThroughRoadStopDirection(ta, ROAD_X))
|
|
ddir = STATIONDIR_Y;
|
|
} else {
|
|
if (!CheckDriveThroughRoadStopDirection(ta, ROAD_Y))
|
|
ddir = STATIONDIR_X;
|
|
}
|
|
} else if (ddir == STATIONDIR_AUTO) {
|
|
ddir = AddAutodetectionRotation(AutodetectRoadObjectDirection(start_tile, pt, _cur_roadtype));
|
|
} else if (ddir == STATIONDIR_AUTO_XY) {
|
|
ddir = AddAutodetectionRotation(AutodetectDriveThroughRoadStopDirection(ta, pt, _cur_roadtype));
|
|
}
|
|
}
|
|
_thd.cm_new = ObjectHighlight::make_road_stop(start_tile, end_tile, _cur_roadtype, ddir, _thd.select_proc == DDSP_BUILD_TRUCKSTOP);
|
|
}
|
|
}
|
|
new_drawstyle = HT_RECT;
|
|
}
|
|
if (_thd.cm != _thd.cm_new) {
|
|
_thd.cm.MarkDirty();
|
|
_thd.cm = _thd.cm_new;
|
|
_thd.cm.MarkDirty();
|
|
}
|
|
return new_drawstyle;
|
|
}
|
|
|
|
void AllocateZoningMap(uint map_size) {
|
|
free(_mz);
|
|
_mz = CallocT<TileZoning>(map_size);
|
|
}
|
|
|
|
uint8 GetTownZone(Town *town, TileIndex tile) {
|
|
auto dist = DistanceSquare(tile, town->xy);
|
|
if (dist > town->cache.squared_town_zone_radius[HZB_TOWN_EDGE])
|
|
return 0;
|
|
|
|
uint8 z = 1;
|
|
for (HouseZonesBits i = HZB_TOWN_OUTSKIRT; i < HZB_END; i++)
|
|
if (dist < town->cache.squared_town_zone_radius[i])
|
|
z = (uint8)i + 1;
|
|
else if (town->cache.squared_town_zone_radius[i] != 0)
|
|
break;
|
|
return z;
|
|
}
|
|
|
|
uint8 GetAnyTownZone(TileIndex tile) {
|
|
HouseZonesBits next_zone = HZB_BEGIN;
|
|
uint8 z = 0;
|
|
|
|
for (Town *town : Town::Iterate()) {
|
|
uint dist = DistanceSquare(tile, town->xy);
|
|
// town code uses <= for checking town borders (tz0) but < for other zones
|
|
while (next_zone < HZB_END
|
|
&& (town->cache.squared_town_zone_radius[next_zone] == 0
|
|
|| dist <= town->cache.squared_town_zone_radius[next_zone] - (next_zone == HZB_BEGIN ? 0 : 1))
|
|
) {
|
|
if (town->cache.squared_town_zone_radius[next_zone] != 0) z = (uint8)next_zone + 1;
|
|
next_zone++;
|
|
}
|
|
}
|
|
return z;
|
|
}
|
|
|
|
void UpdateTownZoning(Town *town, uint32 prev_edge) {
|
|
auto edge = town->cache.squared_town_zone_radius[HZB_TOWN_EDGE];
|
|
if (prev_edge && edge == prev_edge)
|
|
return;
|
|
|
|
auto area = OrthogonalTileArea(town->xy, 1, 1);
|
|
bool recalc;
|
|
if (edge < prev_edge) {
|
|
area.Expand(IntSqrt(prev_edge));
|
|
recalc = true;
|
|
} else {
|
|
area.Expand(IntSqrt(edge));
|
|
recalc = false;
|
|
}
|
|
// TODO mark dirty only if zoning is on
|
|
for(TileIndex tile : area) {
|
|
uint8 group = GetTownZone(town, tile);
|
|
|
|
if (_mz[tile].town_zone != group)
|
|
_mz[tile].industry_fund_result = 0;
|
|
|
|
if (_mz[tile].town_zone > group) {
|
|
if (recalc) {
|
|
_mz[tile].town_zone = GetAnyTownZone(tile);
|
|
if (_zoning.outer == CHECKTOWNZONES)
|
|
MarkTileDirtyByTile(tile);
|
|
}
|
|
} else if (_mz[tile].town_zone < group) {
|
|
_mz[tile].town_zone = group;
|
|
if (_zoning.outer == CHECKTOWNZONES)
|
|
MarkTileDirtyByTile(tile);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UpdateAdvertisementZoning(TileIndex center, uint radius, uint8 zone) {
|
|
uint16 x1, y1, x2, y2;
|
|
x1 = (uint16)std::max<int>(0, TileX(center) - radius);
|
|
x2 = (uint16)std::min<int>(TileX(center) + radius + 1, MapSizeX());
|
|
y1 = (uint16)std::max<int>(0, TileY(center) - radius);
|
|
y2 = (uint16)std::min<int>(TileY(center) + radius + 1, MapSizeY());
|
|
for (uint16 y = y1; y < y2; y++) {
|
|
for (uint16 x = x1; x < x2; x++) {
|
|
auto tile = TileXY(x, y);
|
|
if (DistanceManhattan(tile, center) > radius) continue;
|
|
_mz[tile].advertisement_zone = std::max(_mz[tile].advertisement_zone, zone);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UpdateZoningTownHouses(const Town *town, uint32 old_houses) {
|
|
if (!town->larger_town)
|
|
return;
|
|
_town_cache.erase(std::make_pair(old_houses, town));
|
|
_town_cache.insert(std::make_pair(town->cache.num_houses, town));
|
|
}
|
|
|
|
void InitializeZoningMap() {
|
|
_town_cache.clear();
|
|
for (Town *t : Town::Iterate()) {
|
|
UpdateTownZoning(t, 0);
|
|
UpdateAdvertisementZoning(t->xy, 10, 3);
|
|
UpdateAdvertisementZoning(t->xy, 15, 2);
|
|
UpdateAdvertisementZoning(t->xy, 20, 1);
|
|
UpdateZoningTownHouses(t, 0);
|
|
if (!t->larger_town)
|
|
_town_cache.insert(std::make_pair(t->cache.num_houses, t));
|
|
}
|
|
}
|
|
|
|
std::pair<ZoningBorder, uint8> GetTownZoneBorder(TileIndex tile) {
|
|
return CalcTileBorders(tile, [](TileIndex t) { return _mz[t].town_zone; });
|
|
}
|
|
|
|
ZoningBorder GetAnyStationCatchmentBorder(TileIndex tile) {
|
|
ZoningBorder border = ZoningBorder::NONE;
|
|
StationFinder morestations(TileArea(tile, 1, 1));
|
|
for (Station *st: *morestations.GetStations()) {
|
|
border |= CalcTileBorders(tile, [st](TileIndex t) {return st->TileIsInCatchment(t) ? 1 : 0; }).first;
|
|
}
|
|
if (border & ZoningBorder::TOP_CORNER && border & (ZoningBorder::TOP_LEFT | ZoningBorder::TOP_RIGHT))
|
|
border &= ~ZoningBorder::TOP_CORNER;
|
|
return border;
|
|
}
|
|
|
|
void SetIndustryForbiddenTilesHighlight(IndustryType type) {
|
|
if (_settings_client.gui.cm_show_industry_forbidden_tiles &&
|
|
_industry_forbidden_tiles != type) {
|
|
MarkWholeScreenDirty();
|
|
}
|
|
_industry_forbidden_tiles = type;
|
|
UpdateIndustryHighlight();
|
|
}
|
|
|
|
|
|
PaletteID GetTreeShadePal(TileIndex tile) {
|
|
if (_settings_client.gui.cm_shaded_trees != 1)
|
|
return PAL_NONE;
|
|
|
|
Slope slope = GetTileSlope(tile);
|
|
switch (slope) {
|
|
case SLOPE_STEEP_N:
|
|
case SLOPE_N:
|
|
return CM_PALETTE_SHADE_S;
|
|
|
|
case SLOPE_NE:
|
|
return CM_PALETTE_SHADE_SW;
|
|
|
|
case SLOPE_E:
|
|
case SLOPE_STEEP_E:
|
|
return CM_PALETTE_SHADE_W;
|
|
|
|
case SLOPE_SE:
|
|
return CM_PALETTE_SHADE_NW;
|
|
|
|
case SLOPE_STEEP_S:
|
|
case SLOPE_S:
|
|
return CM_PALETTE_SHADE_N;
|
|
|
|
case SLOPE_SW:
|
|
return CM_PALETTE_SHADE_NE;
|
|
|
|
case SLOPE_STEEP_W:
|
|
case SLOPE_W:
|
|
return CM_PALETTE_SHADE_E;
|
|
|
|
case SLOPE_NW:
|
|
return CM_PALETTE_SHADE_SE;
|
|
|
|
default:
|
|
return PAL_NONE;
|
|
}
|
|
}
|
|
|
|
} // namespace citymania
|