diff --git a/projects/openttd_vs140.vcxproj b/projects/openttd_vs140.vcxproj
--- a/projects/openttd_vs140.vcxproj
+++ b/projects/openttd_vs140.vcxproj
@@ -680,6 +680,7 @@
+
diff --git a/projects/openttd_vs140.vcxproj.filters b/projects/openttd_vs140.vcxproj.filters
--- a/projects/openttd_vs140.vcxproj.filters
+++ b/projects/openttd_vs140.vcxproj.filters
@@ -1128,6 +1128,9 @@
Header Files
+
+ Header Files
+
Header Files
diff --git a/projects/openttd_vs141.vcxproj b/projects/openttd_vs141.vcxproj
--- a/projects/openttd_vs141.vcxproj
+++ b/projects/openttd_vs141.vcxproj
@@ -680,6 +680,7 @@
+
diff --git a/projects/openttd_vs141.vcxproj.filters b/projects/openttd_vs141.vcxproj.filters
--- a/projects/openttd_vs141.vcxproj.filters
+++ b/projects/openttd_vs141.vcxproj.filters
@@ -1128,6 +1128,9 @@
Header Files
+
+ Header Files
+
Header Files
diff --git a/projects/openttd_vs142.vcxproj b/projects/openttd_vs142.vcxproj
--- a/projects/openttd_vs142.vcxproj
+++ b/projects/openttd_vs142.vcxproj
@@ -680,6 +680,7 @@
+
diff --git a/projects/openttd_vs142.vcxproj.filters b/projects/openttd_vs142.vcxproj.filters
--- a/projects/openttd_vs142.vcxproj.filters
+++ b/projects/openttd_vs142.vcxproj.filters
@@ -1128,6 +1128,9 @@
Header Files
+
+ Header Files
+
Header Files
diff --git a/source.list b/source.list
--- a/source.list
+++ b/source.list
@@ -367,6 +367,7 @@ timetable.h
toolbar_gui.h
town.h
town_type.h
+town_kdtree.h
townname_func.h
townname_type.h
track_func.h
diff --git a/src/misc.cpp b/src/misc.cpp
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -28,6 +28,7 @@
#include "core/pool_type.hpp"
#include "game/game.hpp"
#include "linkgraph/linkgraphschedule.h"
+#include "town_kdtree.h"
#include "safeguards.h"
@@ -75,6 +76,8 @@ void InitializeGame(uint size_x, uint si
LinkGraphSchedule::Clear();
PoolBase::Clean(PT_NORMAL);
+ RebuildTownKdtree();
+
ResetPersistentNewGRFData();
InitializeSound();
diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -536,6 +536,8 @@ bool AfterLoadGame()
GamelogTestRevision();
GamelogTestMode();
+ RebuildTownKdtree();
+
if (IsSavegameVersionBefore(SLV_98)) GamelogGRFAddList(_grfconfig);
if (IsSavegameVersionBefore(SLV_119)) {
diff --git a/src/saveload/town_sl.cpp b/src/saveload/town_sl.cpp
--- a/src/saveload/town_sl.cpp
+++ b/src/saveload/town_sl.cpp
@@ -28,6 +28,7 @@ void RebuildTownCaches()
{
Town *town;
InitializeBuildingCounts();
+ RebuildTownKdtree();
/* Reset town population and num_houses */
FOR_ALL_TOWNS(town) {
diff --git a/src/town.h b/src/town.h
--- a/src/town.h
+++ b/src/town.h
@@ -145,6 +145,9 @@ void UpdateAllTownVirtCoords();
void ShowTownViewWindow(TownID town);
void ExpandTown(Town *t);
+void RebuildTownKdtree();
+
+
/**
* Action types that a company must ask permission for to a town authority.
* @see CheckforTownRating
diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp
--- a/src/town_cmd.cpp
+++ b/src/town_cmd.cpp
@@ -38,6 +38,7 @@
#include "subsidy_func.h"
#include "core/pool_func.hpp"
#include "town.h"
+#include "town_kdtree.h"
#include "townname_func.h"
#include "core/random_func.hpp"
#include "core/backup_type.hpp"
@@ -59,6 +60,20 @@ CargoTypes _town_cargoes_accepted; ///<
TownPool _town_pool("Town");
INSTANTIATE_POOL_METHODS(Town)
+
+TownKdtree _town_kdtree(&Kdtree_TownXYFunc);
+
+void RebuildTownKdtree()
+{
+ std::vector townids;
+ Town *town;
+ FOR_ALL_TOWNS(town) {
+ townids.push_back(town->index);
+ }
+ _town_kdtree.Build(townids.begin(), townids.end());
+}
+
+
/**
* Check if a town 'owns' a bridge.
* Bridges to not directly have an owner, so we check the tiles adjacent to the bridge ends.
@@ -366,30 +381,9 @@ static void AnimateTile_Town(TileIndex t
*/
static bool IsCloseToTown(TileIndex tile, uint dist)
{
- /* On a large map with many towns, it may be faster to check the surroundings of the tile.
- * An iteration in TILE_AREA_LOOP() is generally 2 times faster than one in FOR_ALL_TOWNS(). */
- if (Town::GetNumItems() > (size_t) (dist * dist * 2)) {
- const int tx = TileX(tile);
- const int ty = TileY(tile);
- TileArea tile_area = TileArea(
- TileXY(max(0, tx - (int) dist), max(0, ty - (int) dist)),
- TileXY(min(MapMaxX(), tx + (int) dist), min(MapMaxY(), ty + (int) dist))
- );
- TILE_AREA_LOOP(atile, tile_area) {
- if (GetTileType(atile) == MP_HOUSE) {
- Town *t = Town::GetByTile(atile);
- if (DistanceManhattan(tile, t->xy) < dist) return true;
- }
- }
- return false;
- }
-
- const Town *t;
-
- FOR_ALL_TOWNS(t) {
- if (DistanceManhattan(tile, t->xy) < dist) return true;
- }
- return false;
+ if (_town_kdtree.Count() == 0) return false;
+ Town *t = Town::Get(_town_kdtree.FindNearest(TileX(tile), TileY(tile)));
+ return DistanceManhattan(tile, t->xy) < dist;
}
/**
@@ -1682,6 +1676,8 @@ static void DoCreateTown(Town *t, TileIn
t->grow_counter = t->index % TOWN_GROWTH_TICKS;
t->growth_rate = TownTicksToGameTicks(250);
+ _town_kdtree.Insert(t->index);
+
/* Set the default cargo requirement for town growth */
switch (_settings_game.game_creation.landscape) {
case LT_ARCTIC:
@@ -2092,6 +2088,9 @@ bool GenerateTowns(TownLayout layout)
town_names.clear();
+ /* Build the town k-d tree again to make sure it's well balanced */
+ RebuildTownKdtree();
+
if (current_number != 0) return true;
/* If current_number is still zero at this point, it means that not a single town has been created.
@@ -2867,7 +2866,10 @@ CommandCost CmdDeleteTown(TileIndex tile
}
/* The town destructor will delete the other things related to the town. */
- if (flags & DC_EXEC) delete t;
+ if (flags & DC_EXEC) {
+ _town_kdtree.Remove(t->index);
+ delete t;
+ }
return CommandCost();
}
@@ -3392,19 +3394,12 @@ CommandCost CheckIfAuthorityAllowsNewSta
*/
Town *CalcClosestTownFromTile(TileIndex tile, uint threshold)
{
- Town *t;
- uint best = threshold;
- Town *best_town = NULL;
-
- FOR_ALL_TOWNS(t) {
- uint dist = DistanceManhattan(tile, t->xy);
- if (dist < best) {
- best = dist;
- best_town = t;
- }
- }
-
- return best_town;
+ if (Town::GetNumItems() == 0) return NULL;
+
+ TownID tid = _town_kdtree.FindNearest(TileX(tile), TileY(tile));
+ Town *town = Town::Get(tid);
+ if (DistanceManhattan(tile, town->xy) < threshold) return town;
+ return NULL;
}
/**
diff --git a/src/town_kdtree.h b/src/town_kdtree.h
new file mode 100644
--- /dev/null
+++ b/src/town_kdtree.h
@@ -0,0 +1,20 @@
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/** @file town_kdtree.h Declarations for accessing the k-d tree of towns */
+
+#ifndef TOWN_KDTREE_H
+#define TOWN_KDTREE_H
+
+#include "core/kdtree.hpp"
+#include "town.h"
+
+inline uint16 Kdtree_TownXYFunc(TownID tid, int dim) { return (dim == 0) ? TileX(Town::Get(tid)->xy) : TileY(Town::Get(tid)->xy); }
+typedef Kdtree TownKdtree;
+extern TownKdtree _town_kdtree;
+
+#endif