mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-03-11 01:49:50 +00:00
(svn r14591) -Fix [FS#2388](r14528): cached nearest town could be invalid after importing older savegame and during town generation
-Codechange: rewrite parts of code responsible for caching index of nearest town
This commit is contained in:
parent
e1a36e90a4
commit
07e3c096b3
@ -513,7 +513,7 @@
|
|||||||
<td valign=top nowrap> </td>
|
<td valign=top nowrap> </td>
|
||||||
<td>
|
<td>
|
||||||
<ul>
|
<ul>
|
||||||
<li>m2: Index into the array of towns (owning town for town roads; closest town otherwise, INVALID_TOWN if not yet calculated)</li>
|
<li>m2: Index into the array of towns (owning town for town roads; closest town otherwise, INVALID_TOWN if there is no town or we are creating a town)</li>
|
||||||
<li>m3 bit 7 set = on snow or desert</li>
|
<li>m3 bit 7 set = on snow or desert</li>
|
||||||
<li>m7 bits 7..5: present road types
|
<li>m7 bits 7..5: present road types
|
||||||
<table>
|
<table>
|
||||||
|
@ -2563,7 +2563,7 @@ bool AfterLoadGame()
|
|||||||
|
|
||||||
if (CheckSavegameVersion(103)) {
|
if (CheckSavegameVersion(103)) {
|
||||||
/* Non-town-owned roads now store the closest town */
|
/* Non-town-owned roads now store the closest town */
|
||||||
InvalidateTownForRoadTile();
|
UpdateNearestTownForRoadTiles(false);
|
||||||
|
|
||||||
/* signs with invalid owner left from older savegames */
|
/* signs with invalid owner left from older savegames */
|
||||||
Sign *si;
|
Sign *si;
|
||||||
|
@ -319,9 +319,9 @@ static CommandCost RemoveRoad(TileIndex tile, uint32 flags, RoadBits pieces, Roa
|
|||||||
DoClearSquare(tile);
|
DoClearSquare(tile);
|
||||||
} else {
|
} else {
|
||||||
if (rt == ROADTYPE_ROAD && IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN)) {
|
if (rt == ROADTYPE_ROAD && IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN)) {
|
||||||
/* Promote ownership from tram or highway and invalidate town index */
|
/* Update nearest-town index */
|
||||||
SetRoadOwner(tile, ROADTYPE_ROAD, GetRoadOwner(tile, (HasBit(rts, ROADTYPE_TRAM) ? ROADTYPE_TRAM : ROADTYPE_HWAY)));
|
const Town *town = CalcClosestTownFromTile(tile, UINT_MAX);
|
||||||
SetTownIndex(tile, (TownID)INVALID_TOWN);
|
SetTownIndex(tile, town == NULL ? (TownID)INVALID_TOWN : town->index);
|
||||||
}
|
}
|
||||||
SetRoadBits(tile, ROAD_NONE, rt);
|
SetRoadBits(tile, ROAD_NONE, rt);
|
||||||
SetRoadTypes(tile, rts);
|
SetRoadTypes(tile, rts);
|
||||||
@ -348,7 +348,7 @@ static CommandCost RemoveRoad(TileIndex tile, uint32 flags, RoadBits pieces, Roa
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Don't allow road to be removed from the crossing when there is tram;
|
/* Don't allow road to be removed from the crossing when there is tram;
|
||||||
* we can't draw the crossing without trambits ;) */
|
* we can't draw the crossing without roadbits ;) */
|
||||||
if (rt == ROADTYPE_ROAD && HasTileRoadType(tile, ROADTYPE_TRAM) && (flags & DC_EXEC || crossing_check)) return CMD_ERROR;
|
if (rt == ROADTYPE_ROAD && HasTileRoadType(tile, ROADTYPE_TRAM) && (flags & DC_EXEC || crossing_check)) return CMD_ERROR;
|
||||||
|
|
||||||
if (flags & DC_EXEC) {
|
if (flags & DC_EXEC) {
|
||||||
@ -1274,14 +1274,22 @@ void DrawRoadDepotSprite(int x, int y, DiagDirection dir, RoadType rt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InvalidateTownForRoadTile()
|
/** Updates cached nearest town for all road tiles
|
||||||
|
* @param invalidate are we just invalidating cached data?
|
||||||
|
* @pre invalidate == true implies _generating_world == true
|
||||||
|
*/
|
||||||
|
void UpdateNearestTownForRoadTiles(bool invalidate)
|
||||||
{
|
{
|
||||||
TileIndex map_size = MapSize();
|
assert(!invalidate || _generating_world);
|
||||||
|
|
||||||
for (TileIndex t = 0; t < map_size; t++) {
|
for (TileIndex t = 0; t < MapSize(); t++) {
|
||||||
if (IsTileType(t, MP_ROAD) && GetRoadOwner(t, ROADTYPE_ROAD) != OWNER_TOWN) {
|
if (IsTileType(t, MP_ROAD) && !HasTownOwnedRoad(t)) {
|
||||||
/* GetRoadOwner(t, ROADTYPE_ROAD) is valid for road tiles even when there is no road */
|
TownID tid = (TownID)INVALID_TOWN;
|
||||||
SetTownIndex(t, (TownID)INVALID_TOWN);
|
if (!invalidate) {
|
||||||
|
const Town *town = CalcClosestTownFromTile(t, UINT_MAX);
|
||||||
|
if (town != NULL) tid = town->index;
|
||||||
|
}
|
||||||
|
SetTownIndex(t, tid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,6 @@
|
|||||||
#include "direction_type.h"
|
#include "direction_type.h"
|
||||||
|
|
||||||
void DrawRoadDepotSprite(int x, int y, DiagDirection dir, RoadType rt);
|
void DrawRoadDepotSprite(int x, int y, DiagDirection dir, RoadType rt);
|
||||||
void InvalidateTownForRoadTile();
|
void UpdateNearestTownForRoadTiles(bool invalidate);
|
||||||
|
|
||||||
#endif /* ROAD_CMD_H */
|
#endif /* ROAD_CMD_H */
|
||||||
|
@ -189,10 +189,21 @@ static inline void SetRoadOwner(TileIndex t, RoadType rt, Owner o)
|
|||||||
|
|
||||||
static inline bool IsRoadOwner(TileIndex t, RoadType rt, Owner o)
|
static inline bool IsRoadOwner(TileIndex t, RoadType rt, Owner o)
|
||||||
{
|
{
|
||||||
assert(rt == ROADTYPE_ROAD || HasTileRoadType(t, rt));
|
assert(HasTileRoadType(t, rt));
|
||||||
return (GetRoadOwner(t, rt) == o);
|
return (GetRoadOwner(t, rt) == o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Checks if given tile has town owned road
|
||||||
|
* @param t tile to check
|
||||||
|
* @return true iff tile has road and the road is owned by a town
|
||||||
|
* @pre IsTileType(t, MP_ROAD)
|
||||||
|
*/
|
||||||
|
static inline bool HasTownOwnedRoad(TileIndex t)
|
||||||
|
{
|
||||||
|
assert(IsTileType(t, MP_ROAD));
|
||||||
|
return HasTileRoadType(t, ROADTYPE_ROAD) && IsRoadOwner(t, ROADTYPE_ROAD, OWNER_TOWN);
|
||||||
|
}
|
||||||
|
|
||||||
/** Which directions are disallowed ? */
|
/** Which directions are disallowed ? */
|
||||||
enum DisallowedRoadDirections {
|
enum DisallowedRoadDirections {
|
||||||
DRD_NONE, ///< None of the directions are disallowed
|
DRD_NONE, ///< None of the directions are disallowed
|
||||||
|
@ -94,12 +94,12 @@ Town::~Town()
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case MP_ROAD:
|
case MP_ROAD:
|
||||||
if (!IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN) && GetTownIndex(tile) == this->index) {
|
/* Cached nearest town is updated later (after this town has been deleted) */
|
||||||
/* Town-owned roads get cleared soon, anyway */
|
if (HasTownOwnedRoad(tile) && GetTownIndex(tile) == this->index) {
|
||||||
SetTownIndex(tile, (TownID)INVALID_TOWN);
|
DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
/* Fall-through */
|
break;
|
||||||
|
|
||||||
case MP_TUNNELBRIDGE:
|
case MP_TUNNELBRIDGE:
|
||||||
if (IsTileOwner(tile, OWNER_TOWN) &&
|
if (IsTileOwner(tile, OWNER_TOWN) &&
|
||||||
ClosestTownFromTile(tile, UINT_MAX) == this)
|
ClosestTownFromTile(tile, UINT_MAX) == this)
|
||||||
@ -116,6 +116,8 @@ Town::~Town()
|
|||||||
MarkWholeScreenDirty();
|
MarkWholeScreenDirty();
|
||||||
|
|
||||||
this->xy = 0;
|
this->xy = 0;
|
||||||
|
|
||||||
|
UpdateNearestTownForRoadTiles(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1564,8 +1566,9 @@ CommandCost CmdBuildTown(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
|
|||||||
if (flags & DC_EXEC) {
|
if (flags & DC_EXEC) {
|
||||||
Town *t = new Town(tile);
|
Town *t = new Town(tile);
|
||||||
_generating_world = true;
|
_generating_world = true;
|
||||||
|
UpdateNearestTownForRoadTiles(true);
|
||||||
DoCreateTown(t, tile, townnameparts, (TownSizeMode)p2, p1);
|
DoCreateTown(t, tile, townnameparts, (TownSizeMode)p2, p1);
|
||||||
InvalidateTownForRoadTile();
|
UpdateNearestTownForRoadTiles(false);
|
||||||
_generating_world = false;
|
_generating_world = false;
|
||||||
}
|
}
|
||||||
return CommandCost();
|
return CommandCost();
|
||||||
@ -2471,26 +2474,32 @@ Town *CalcClosestTownFromTile(TileIndex tile, uint threshold)
|
|||||||
|
|
||||||
Town *ClosestTownFromTile(TileIndex tile, uint threshold)
|
Town *ClosestTownFromTile(TileIndex tile, uint threshold)
|
||||||
{
|
{
|
||||||
if (IsTileType(tile, MP_HOUSE) || (
|
switch (GetTileType(tile)) {
|
||||||
IsTileType(tile, MP_ROAD) && HasTileRoadType(tile, ROADTYPE_ROAD) &&
|
case MP_ROAD:
|
||||||
IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN)
|
if (!HasTownOwnedRoad(tile)) {
|
||||||
)) {
|
TownID tid = GetTownIndex(tile);
|
||||||
return GetTownByTile(tile);
|
if (tid == (TownID)INVALID_TOWN) {
|
||||||
} else if (IsTileType(tile, MP_ROAD)) {
|
/* in the case we are generating "many random towns", this value may be INVALID_TOWN */
|
||||||
TownID town_id = GetTownIndex(tile);
|
if (_generating_world) CalcClosestTownFromTile(tile, threshold);
|
||||||
Town *town;
|
assert(GetNumTowns() == 0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (town_id == INVALID_TOWN) {
|
Town *town = GetTown(tid);
|
||||||
town = CalcClosestTownFromTile(tile, UINT_MAX);
|
assert(town->IsValid());
|
||||||
if (town != NULL) SetTownIndex(tile, town->index);
|
assert(town == CalcClosestTownFromTile(tile, UINT_MAX));
|
||||||
} else {
|
|
||||||
town = GetTown(town_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (town != NULL && town->IsValid() && DistanceManhattan(tile, town->xy) < threshold) return town;
|
if (DistanceManhattan(tile, town->xy) >= threshold) town = NULL;
|
||||||
return NULL;
|
|
||||||
} else {
|
return town;
|
||||||
return CalcClosestTownFromTile(tile, threshold);
|
}
|
||||||
|
/* FALL THROUGH */
|
||||||
|
|
||||||
|
case MP_HOUSE:
|
||||||
|
return GetTownByTile(tile);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return CalcClosestTownFromTile(tile, threshold);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -725,14 +725,15 @@ public:
|
|||||||
|
|
||||||
this->HandleButtonClick(TSEW_RANDOMTOWN);
|
this->HandleButtonClick(TSEW_RANDOMTOWN);
|
||||||
_generating_world = true;
|
_generating_world = true;
|
||||||
|
UpdateNearestTownForRoadTiles(true);
|
||||||
t = CreateRandomTown(20, mode, size);
|
t = CreateRandomTown(20, mode, size);
|
||||||
|
UpdateNearestTownForRoadTiles(false);
|
||||||
_generating_world = false;
|
_generating_world = false;
|
||||||
|
|
||||||
if (t == NULL) {
|
if (t == NULL) {
|
||||||
ShowErrorMessage(STR_NO_SPACE_FOR_TOWN, STR_CANNOT_GENERATE_TOWN, 0, 0);
|
ShowErrorMessage(STR_NO_SPACE_FOR_TOWN, STR_CANNOT_GENERATE_TOWN, 0, 0);
|
||||||
} else {
|
} else {
|
||||||
ScrollMainWindowToTile(t->xy);
|
ScrollMainWindowToTile(t->xy);
|
||||||
InvalidateTownForRoadTile();
|
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
@ -740,11 +741,11 @@ public:
|
|||||||
this->HandleButtonClick(TSEW_MANYRANDOMTOWNS);
|
this->HandleButtonClick(TSEW_MANYRANDOMTOWNS);
|
||||||
|
|
||||||
_generating_world = true;
|
_generating_world = true;
|
||||||
|
UpdateNearestTownForRoadTiles(true);
|
||||||
if (!GenerateTowns()) {
|
if (!GenerateTowns()) {
|
||||||
ShowErrorMessage(STR_NO_SPACE_FOR_TOWN, STR_CANNOT_GENERATE_TOWN, 0, 0);
|
ShowErrorMessage(STR_NO_SPACE_FOR_TOWN, STR_CANNOT_GENERATE_TOWN, 0, 0);
|
||||||
} else {
|
|
||||||
InvalidateTownForRoadTile();
|
|
||||||
}
|
}
|
||||||
|
UpdateNearestTownForRoadTiles(false);
|
||||||
_generating_world = false;
|
_generating_world = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user