diff --git a/bin/data/cmclient-1.grf b/bin/data/cmclient-1.grf new file mode 100644 index 0000000000..4f3206584b Binary files /dev/null and b/bin/data/cmclient-1.grf differ diff --git a/source.list b/source.list index 25bf521f25..cc379d67cb 100644 --- a/source.list +++ b/source.list @@ -614,6 +614,8 @@ zoning_cmd.cpp bitstream.cpp citymania/base64.h citymania/base64.cpp +citymania/zoning.hpp +citymania/zoning.cpp newgrf_revisions.hpp # Save/Load handlers diff --git a/src/citymania/zoning.cpp b/src/citymania/zoning.cpp new file mode 100644 index 0000000000..428bd834c5 --- /dev/null +++ b/src/citymania/zoning.cpp @@ -0,0 +1,124 @@ +#include "../stdafx.h" + +#include "zoning.hpp" + +#include "../house.h" +#include "../core/math_func.hpp" +#include "../town.h" +#include "../tilearea_type.h" +#include "../viewport_func.h" +#include "../zoning.h" + +namespace citymania { + +struct TileZoning { + uint8 town_zone : 3; +}; + +TileZoning *_mz = nullptr; + +void AllocateZoningMap(uint map_size) { + free(_mz); + _mz = CallocT(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; + + Town *town; + FOR_ALL_TOWNS(town) { + 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 + TILE_AREA_LOOP(tile, area) { + uint8 group = GetTownZone(town, tile); + 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 InitializeZoningMap() { + Town *town; + FOR_ALL_TOWNS(town) { + UpdateTownZoning(town, 0); + } +} + +template +uint8 Get(uint32 x, uint32 y, F getter) { + if (x >= MapSizeX() || y >= MapSizeY()) return 0; + return getter(_mz[TileXY(x, y)]); +} + +std::pair GetTownZoneBorder(TileIndex tile) { + auto x = TileX(tile), y = TileY(tile); + ZoningBorder res = ZoningBorder::NONE; + auto z = _mz[tile].town_zone; + if (z == 0) + return std::make_pair(res, 0); + auto getter = [](TileZoning tz) { return tz.town_zone; }; + 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); +} + +} // namespace citymania diff --git a/src/citymania/zoning.hpp b/src/citymania/zoning.hpp new file mode 100644 index 0000000000..f21b8c25ac --- /dev/null +++ b/src/citymania/zoning.hpp @@ -0,0 +1,43 @@ +#ifndef CITYMANIA_ZONING_HPP +#define CITYMANIA_ZONING_HPP + +#include "../core/enum_type.hpp" +#include "../tile_type.h" +#include "../town_type.h" + +namespace citymania { + +////enum class ZoningBorder : unt8 { +enum ZoningBorder: uint8 { + NONE = 0, + TOP_LEFT = 1, + TOP_RIGHT = 2, + BOTTOM_RIGHT = 4, + BOTTOM_LEFT = 8, + TOP_CORNER = 16, + RIGHT_CORNER = 32, + BOTTOM_CORNER = 64, + LEFT_CORNER = 128, +}; + +DECLARE_ENUM_AS_BIT_SET(ZoningBorder); + +// enum class AdvertisementZone: uint8 { +// NONE = 0, +// LARGE = 1, +// MEDIUM = 2, +// SMALL = 3, +// }; + + +void AllocateZoningMap(uint map_size); +void InitializeZoningMap(); + +void UpdateTownZoning(Town *town, uint32 prev_edge); + +std::pair GetTownZoneBorder(TileIndex tile); +std::pair GetTownAdvertisementBorder(TileIndex tile); + +} // namespace citymania + +#endif diff --git a/src/gfxinit.cpp b/src/gfxinit.cpp index 677a442993..ac691013fd 100644 --- a/src/gfxinit.cpp +++ b/src/gfxinit.cpp @@ -192,7 +192,8 @@ static void LoadSpriteTables() i++ ); } - LoadGrfFile("innerhighlight.grf", SPR_INNER_HIGHLIGHT_BASE, i++); + // LoadGrfFile("innerhighlight.grf", SPR_INNER_HIGHLIGHT_BASE, i++); + LoadGrfFile("cmclient-1.grf", SPR_INNER_HIGHLIGHT_BASE - 4, i++); /* Initialize the unicode to sprite mapping table */ InitializeUnicodeGlyphMap(); diff --git a/src/map.cpp b/src/map.cpp index 53e26ed73e..dd2c175034 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -14,6 +14,7 @@ #include "core/alloc_func.hpp" #include "water_map.h" #include "string_func.h" +#include "citymania/zoning.hpp" #include "safeguards.h" @@ -63,6 +64,7 @@ void AllocateMap(uint size_x, uint size_y) _m = CallocT(_map_size); _me = CallocT(_map_size); + citymania::AllocateZoningMap(_map_size); } diff --git a/src/openttd.cpp b/src/openttd.cpp index 8ca080989f..e3e7ecb4db 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -69,6 +69,8 @@ #include "linkgraph/linkgraphschedule.h" +#include "citymania/zoning.hpp" + #include #include @@ -962,6 +964,7 @@ static void MakeNewGameDone() CheckEngines(); CheckIndustries(); + citymania::InitializeZoningMap(); MarkWholeScreenDirty(); } @@ -978,6 +981,7 @@ static void MakeNewGame(bool from_heightmap, bool reset_settings) static void MakeNewEditorWorldDone() { SetLocalCompany(OWNER_NONE); + citymania::InitializeZoningMap(); } static void MakeNewEditorWorld() diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 53e39c0852..5dbac22448 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -63,6 +63,8 @@ #include "../game/game_text.hpp" #include "../table/control_codes.h" +#include "../citymania/zoning.hpp" + #include "saveload_internal.h" #include @@ -3267,6 +3269,7 @@ bool AfterLoadGame() AfterLoadLinkGraphs(); AfterLoadFindBTProCBInfo(); + citymania::InitializeZoningMap(); return true; } diff --git a/src/table/sprites.h b/src/table/sprites.h index 95c070ce57..38fc9cf190 100644 --- a/src/table/sprites.h +++ b/src/table/sprites.h @@ -317,8 +317,11 @@ static const SpriteID SPR_PALETTE_ZONING_YELLOW = SPR_INNER_HIGHLIGHT_BASE + static const SpriteID SPR_PALETTE_ZONING_PURPLE = SPR_INNER_HIGHLIGHT_BASE + 28; static const SpriteID SPR_INNER_HIGHLIGHT_COUNT = 29; +static const SpriteID SPR_BORDER_HIGHLIGHT_BASE = SPR_INNER_HIGHLIGHT_BASE + SPR_INNER_HIGHLIGHT_COUNT + 1; +static const SpriteID SPR_BORDER_HIGHLIGHT_COUNT = 19 * 19; + /* From where can we start putting NewGRFs? */ -static const SpriteID SPR_NEWGRFS_BASE = SPR_INNER_HIGHLIGHT_BASE + SPR_INNER_HIGHLIGHT_COUNT; +static const SpriteID SPR_NEWGRFS_BASE = SPR_BORDER_HIGHLIGHT_BASE + SPR_BORDER_HIGHLIGHT_COUNT; /* Manager face sprites */ static const SpriteID SPR_GRADIENT = 874; // background gradient behind manager face diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index d3f9cfd5b5..edadee823a 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -54,6 +54,8 @@ #include "table/strings.h" #include "table/town_land.h" +#include "citymania/zoning.hpp" + #include "safeguards.h" bool _cb_enabled = false; @@ -1816,6 +1818,7 @@ static bool GrowTown(Town *t) void UpdateTownRadius(Town *t) { + auto prev_tz0 = t->cache.squared_town_zone_radius[0]; static const uint32 _town_squared_town_zone_radius_data[23][5] = { { 4, 0, 0, 0, 0}, // 0 { 16, 0, 0, 0, 0}, @@ -1855,6 +1858,8 @@ void UpdateTownRadius(Town *t) t->cache.squared_town_zone_radius[3] = mass * 5 - 5; t->cache.squared_town_zone_radius[4] = mass * 3 + 5; } + if (!_generating_world) + citymania::UpdateTownZoning(t, prev_tz0); } void UpdateTownMaxPass(Town *t) diff --git a/src/zoning_cmd.cpp b/src/zoning_cmd.cpp index 5bdc9124ab..d14a5c919c 100644 --- a/src/zoning_cmd.cpp +++ b/src/zoning_cmd.cpp @@ -9,6 +9,9 @@ #include #include +#include "citymania/zoning.hpp" + + Zoning _zoning = {CHECKNOTHING, CHECKNOTHING}; static const SpriteID INVALID_SPRITE_ID = UINT_MAX; //RED GREEN BLACK LIGHT_BLUE ORANGE WHITE YELLOW PURPLE @@ -268,10 +271,11 @@ SpriteID TileZoneCheckTownZones(TileIndex tile) { Town *town; FOR_ALL_TOWNS(town) { + 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 - || DistanceSquare(tile, town->xy) <= town->cache.squared_town_zone_radius[next_zone] + (next_zone == HZB_BEGIN ? 0 : 1)) + || 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) tz = next_zone; next_zone++; @@ -397,6 +401,38 @@ SpriteID TileZoningSpriteEvaluation(TileIndex tile, Owner owner, EvaluationMode } } +SpriteID GetTownZoneBorderColor(uint8 zone) { + switch (zone) { + default: return SPR_PALETTE_ZONING_LIGHT_BLUE; // Tz0 + case 2: return SPR_PALETTE_ZONING_RED; // Tz1 + case 3: return SPR_PALETTE_ZONING_YELLOW; // Tz2 + case 4: return SPR_PALETTE_ZONING_GREEN; // Tz3 + case 5: return SPR_PALETTE_ZONING_WHITE; // Tz4 - center + }; +} + +void DrawBorderSprites(const TileInfo *ti, citymania::ZoningBorder border, SpriteID color) { + auto b = (uint8)border & 15; + auto tile_sprite = SPR_BORDER_HIGHLIGHT_BASE + _tileh_to_sprite[ti->tileh] * 19; + if (b) { + AddSortableSpriteToDraw(tile_sprite + b - 1, + color, ti->x, ti->y, 0x10, 0x10, 1, ti->z + 7); + } + if (border & citymania::ZoningBorder::TOP_CORNER) + AddSortableSpriteToDraw(tile_sprite + 15, + color, ti->x, ti->y, 0x10, 0x10, 1, ti->z + 7); + if (border & citymania::ZoningBorder::RIGHT_CORNER) + AddSortableSpriteToDraw(tile_sprite + 16, + color, ti->x, ti->y, 0x10, 0x10, 1, ti->z + 7); + if (border & citymania::ZoningBorder::BOTTOM_CORNER) + AddSortableSpriteToDraw(tile_sprite + 17, + color, ti->x, ti->y, 0x10, 0x10, 1, ti->z + 7); + if (border & citymania::ZoningBorder::LEFT_CORNER) + AddSortableSpriteToDraw(tile_sprite + 18, + color, ti->x, ti->y, 0x10, 0x10, 1, ti->z + 7); + +} + /** * Draw the the zoning on the tile. * @param TileInfo ti @@ -406,7 +442,14 @@ void DrawTileZoning(const TileInfo *ti) { if(_zoning.outer == CHECKNOTHING && _zoning.inner == CHECKNOTHING) return; //nothing to do if (_game_mode != GM_NORMAL || ti->tile >= MapSize() || IsTileType(ti->tile, MP_VOID)) return; //check invalid if (_zoning.outer != CHECKNOTHING){ - DrawZoningSprites(SPR_SELECT_TILE, TileZoningSpriteEvaluation(ti->tile, _local_company, _zoning.outer), ti); + if (_zoning.outer == CHECKTOWNZONES) { + auto p = citymania::GetTownZoneBorder(ti->tile); + if (p.first && p.second) { + DrawBorderSprites(ti, p.first, GetTownZoneBorderColor(p.second)); + } + } else { + DrawZoningSprites(SPR_SELECT_TILE, TileZoningSpriteEvaluation(ti->tile, _local_company, _zoning.outer), ti); + } } if (_zoning.inner != CHECKNOTHING){ DrawZoningSprites(SPR_INNER_HIGHLIGHT_BASE, TileZoningSpriteEvaluation(ti->tile, _local_company, _zoning.inner), ti);