Improve town zones outer highlight speed and visuals

This commit is contained in:
dP
2019-11-04 02:27:37 +03:00
parent 0c1a29af7f
commit 43a21e96f4
11 changed files with 234 additions and 4 deletions

BIN
bin/data/cmclient-1.grf Normal file

Binary file not shown.

View File

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

124
src/citymania/zoning.cpp Normal file
View File

@@ -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<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;
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 <typename F>
uint8 Get(uint32 x, uint32 y, F getter) {
if (x >= MapSizeX() || y >= MapSizeY()) return 0;
return getter(_mz[TileXY(x, y)]);
}
std::pair<ZoningBorder, uint8> 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

43
src/citymania/zoning.hpp Normal file
View File

@@ -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<ZoningBorder, uint8> GetTownZoneBorder(TileIndex tile);
std::pair<ZoningBorder, uint8> GetTownAdvertisementBorder(TileIndex tile);
} // namespace citymania
#endif

View File

@@ -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();

View File

@@ -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<Tile>(_map_size);
_me = CallocT<TileExtended>(_map_size);
citymania::AllocateZoningMap(_map_size);
}

View File

@@ -69,6 +69,8 @@
#include "linkgraph/linkgraphschedule.h"
#include "citymania/zoning.hpp"
#include <stdarg.h>
#include <system_error>
@@ -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()

View File

@@ -63,6 +63,8 @@
#include "../game/game_text.hpp"
#include "../table/control_codes.h"
#include "../citymania/zoning.hpp"
#include "saveload_internal.h"
#include <signal.h>
@@ -3267,6 +3269,7 @@ bool AfterLoadGame()
AfterLoadLinkGraphs();
AfterLoadFindBTProCBInfo();
citymania::InitializeZoningMap();
return true;
}

View File

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

View File

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

View File

@@ -9,6 +9,9 @@
#include <algorithm>
#include <vector>
#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);