Changeset - r10340:90705a3f079d
[Not reviewed]
master
0 7 0
smatz - 16 years ago 2008-11-18 22:43:59
smatz@openttd.org
(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
7 files changed with 72 insertions and 43 deletions:
0 comments (0 inline, 0 general)
docs/landscape.html
Show inline comments
 
@@ -510,13 +510,13 @@
 
   <td><strong>Roads</strong></td>
 
  </tr>
 
  <tr>
 
   <td valign=top nowrap>&nbsp;</td>
 
   <td>
 
    <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>m7 bits 7..5: present road types
 
      <table>
 
       <tr>
 
        <td>bit 0&nbsp; </td>
 
        <td>normal road</td>
src/openttd.cpp
Show inline comments
 
@@ -2560,13 +2560,13 @@ bool AfterLoadGame()
 
			if (IsLevelCrossingTile(t)) UpdateLevelCrossing(t, false);
 
		}
 
	}
 

	
 
	if (CheckSavegameVersion(103)) {
 
		/* Non-town-owned roads now store the closest town */
 
		InvalidateTownForRoadTile();
 
		UpdateNearestTownForRoadTiles(false);
 

	
 
		/* signs with invalid owner left from older savegames */
 
		Sign *si;
 
		FOR_ALL_SIGNS(si) {
 
			if (si->owner != OWNER_NONE && !IsValidCompanyID(si->owner)) si->owner = OWNER_NONE;
 
		}
src/road_cmd.cpp
Show inline comments
 
@@ -316,15 +316,15 @@ static CommandCost RemoveRoad(TileIndex 
 
					RoadTypes rts = GetRoadTypes(tile) & ComplementRoadTypes(RoadTypeToRoadTypes(rt));
 
					if (rts == ROADTYPES_NONE) {
 
						/* Includes MarkTileDirtyByTile() */
 
						DoClearSquare(tile);
 
					} else {
 
						if (rt == ROADTYPE_ROAD && IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN)) {
 
							/* Promote ownership from tram or highway and invalidate town index */
 
							SetRoadOwner(tile, ROADTYPE_ROAD, GetRoadOwner(tile, (HasBit(rts, ROADTYPE_TRAM) ? ROADTYPE_TRAM : ROADTYPE_HWAY)));
 
							SetTownIndex(tile, (TownID)INVALID_TOWN);
 
							/* Update nearest-town index */
 
							const Town *town = CalcClosestTownFromTile(tile, UINT_MAX);
 
							SetTownIndex(tile, town == NULL ? (TownID)INVALID_TOWN : town->index);
 
						}
 
						SetRoadBits(tile, ROAD_NONE, rt);
 
						SetRoadTypes(tile, rts);
 
						MarkTileDirtyByTile(tile);
 
					}
 
				} else {
 
@@ -345,13 +345,13 @@ static CommandCost RemoveRoad(TileIndex 
 
		case ROAD_TILE_CROSSING: {
 
			if (pieces & ComplementRoadBits(GetCrossingRoadBits(tile))) {
 
				return CMD_ERROR;
 
			}
 

	
 
			/* 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 (flags & DC_EXEC) {
 
				RoadTypes rts = GetRoadTypes(tile) & ComplementRoadTypes(RoadTypeToRoadTypes(rt));
 
				if (rts == ROADTYPES_NONE) {
 
					TrackBits tracks = GetCrossingRailBits(tile);
 
@@ -1271,20 +1271,28 @@ void DrawRoadDepotSprite(int x, int y, D
 
		SpriteID image = dtss->image.sprite;
 

	
 
		DrawSprite(image, HasBit(image, PALETTE_MODIFIER_COLOR) ? palette : PAL_NONE, x + pt.x, y + pt.y);
 
	}
 
}
 

	
 
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++) {
 
		if (IsTileType(t, MP_ROAD) && GetRoadOwner(t, ROADTYPE_ROAD) != OWNER_TOWN) {
 
			/* GetRoadOwner(t, ROADTYPE_ROAD) is valid for road tiles even when there is no road */
 
			SetTownIndex(t, (TownID)INVALID_TOWN);
 
	for (TileIndex t = 0; t < MapSize(); t++) {
 
		if (IsTileType(t, MP_ROAD) && !HasTownOwnedRoad(t)) {
 
			TownID tid = (TownID)INVALID_TOWN;
 
			if (!invalidate) {
 
				const Town *town = CalcClosestTownFromTile(t, UINT_MAX);
 
				if (town != NULL) tid = town->index;
 
			}
 
			SetTownIndex(t, tid);
 
		}
 
	}
 
}
 

	
 
static uint GetSlopeZ_Road(TileIndex tile, uint x, uint y)
 
{
src/road_cmd.h
Show inline comments
 
@@ -5,9 +5,9 @@
 
#ifndef ROAD_CMD_H
 
#define ROAD_CMD_H
 

	
 
#include "direction_type.h"
 

	
 
void DrawRoadDepotSprite(int x, int y, DiagDirection dir, RoadType rt);
 
void InvalidateTownForRoadTile();
 
void UpdateNearestTownForRoadTiles(bool invalidate);
 

	
 
#endif /* ROAD_CMD_H */
src/road_map.h
Show inline comments
 
@@ -186,16 +186,27 @@ static inline void SetRoadOwner(TileInde
 
		case ROAD_TILE_DEPOT: return SetTileOwner(t, 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);
 
}
 

	
 
/** 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 ? */
 
enum DisallowedRoadDirections {
 
	DRD_NONE,       ///< None of the directions are disallowed
 
	DRD_SOUTHBOUND, ///< All southbound traffic is disallowed
 
	DRD_NORTHBOUND, ///< All northbound traffic is disallowed
 
	DRD_BOTH,       ///< All directions are disallowed
src/town_cmd.cpp
Show inline comments
 
@@ -91,18 +91,18 @@ Town::~Town()
 
		switch (GetTileType(tile)) {
 
			case MP_HOUSE:
 
				if (GetTownByTile(tile) == this) DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
				break;
 

	
 
			case MP_ROAD:
 
				if (!IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN) && GetTownIndex(tile) == this->index) {
 
					/* Town-owned roads get cleared soon, anyway */
 
					SetTownIndex(tile, (TownID)INVALID_TOWN);
 
					break;
 
				/* Cached nearest town is updated later (after this town has been deleted) */
 
				if (HasTownOwnedRoad(tile) && GetTownIndex(tile) == this->index) {
 
					DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
				}
 
				/* Fall-through */
 
				break;
 

	
 
			case MP_TUNNELBRIDGE:
 
				if (IsTileOwner(tile, OWNER_TOWN) &&
 
						ClosestTownFromTile(tile, UINT_MAX) == this)
 
					DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
 
				break;
 

	
 
@@ -113,12 +113,14 @@ Town::~Town()
 

	
 
	DeleteSubsidyWithTown(this->index);
 

	
 
	MarkWholeScreenDirty();
 

	
 
	this->xy = 0;
 

	
 
	UpdateNearestTownForRoadTiles(false);
 
}
 

	
 
/**
 
 * Generate a random town road layout.
 
 *
 
 * The layout is based on the TileHash.
 
@@ -1561,14 +1563,15 @@ CommandCost CmdBuildTown(TileIndex tile,
 
	if (!Town::CanAllocateItem()) return_cmd_error(STR_023A_TOO_MANY_TOWNS);
 

	
 
	/* Create the town */
 
	if (flags & DC_EXEC) {
 
		Town *t = new Town(tile);
 
		_generating_world = true;
 
		UpdateNearestTownForRoadTiles(true);
 
		DoCreateTown(t, tile, townnameparts, (TownSizeMode)p2, p1);
 
		InvalidateTownForRoadTile();
 
		UpdateNearestTownForRoadTiles(false);
 
		_generating_world = false;
 
	}
 
	return CommandCost();
 
}
 

	
 
Town *CreateRandomTown(uint attempts, TownSizeMode mode, uint size)
 
@@ -2468,32 +2471,38 @@ Town *CalcClosestTownFromTile(TileIndex 
 
	return best_town;
 
}
 

	
 

	
 
Town *ClosestTownFromTile(TileIndex tile, uint threshold)
 
{
 
	if (IsTileType(tile, MP_HOUSE) || (
 
				IsTileType(tile, MP_ROAD) && HasTileRoadType(tile, ROADTYPE_ROAD) &&
 
				IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN)
 
			)) {
 
		return GetTownByTile(tile);
 
	} else if (IsTileType(tile, MP_ROAD)) {
 
		TownID town_id = GetTownIndex(tile);
 
		Town *town;
 

	
 
		if (town_id == INVALID_TOWN) {
 
			town = CalcClosestTownFromTile(tile, UINT_MAX);
 
			if (town != NULL) SetTownIndex(tile, town->index);
 
		} else {
 
			town = GetTown(town_id);
 
		}
 

	
 
		if (town != NULL && town->IsValid() && DistanceManhattan(tile, town->xy) < threshold) return town;
 
		return NULL;
 
	} else {
 
		return CalcClosestTownFromTile(tile, threshold);
 
	switch (GetTileType(tile)) {
 
		case MP_ROAD:
 
			if (!HasTownOwnedRoad(tile)) {
 
				TownID tid = GetTownIndex(tile);
 
				if (tid == (TownID)INVALID_TOWN) {
 
					/* in the case we are generating "many random towns", this value may be INVALID_TOWN */
 
					if (_generating_world) CalcClosestTownFromTile(tile, threshold);
 
					assert(GetNumTowns() == 0);
 
					return NULL;
 
				}
 

	
 
				Town *town = GetTown(tid);
 
				assert(town->IsValid());
 
				assert(town == CalcClosestTownFromTile(tile, UINT_MAX));
 

	
 
				if (DistanceManhattan(tile, town->xy) >= threshold) town = NULL;
 

	
 
				return town;
 
			}
 
			/* FALL THROUGH */
 

	
 
		case MP_HOUSE:
 
			return GetTownByTile(tile);
 

	
 
		default:
 
			return CalcClosestTownFromTile(tile, threshold);
 
	}
 
}
 

	
 
static bool _town_rating_test = false;
 
std::map<const Town *, int> _town_test_ratings;
 

	
src/town_gui.cpp
Show inline comments
 
@@ -722,32 +722,33 @@ public:
 
				Town *t;
 
				uint size = min(_scengen_town_size, (int)TSM_CITY);
 
				TownSizeMode mode = _scengen_town_size > TSM_CITY ? TSM_CITY : TSM_FIXED;
 

	
 
				this->HandleButtonClick(TSEW_RANDOMTOWN);
 
				_generating_world = true;
 
				UpdateNearestTownForRoadTiles(true);
 
				t = CreateRandomTown(20, mode, size);
 
				UpdateNearestTownForRoadTiles(false);
 
				_generating_world = false;
 

	
 
				if (t == NULL) {
 
					ShowErrorMessage(STR_NO_SPACE_FOR_TOWN, STR_CANNOT_GENERATE_TOWN, 0, 0);
 
				} else {
 
					ScrollMainWindowToTile(t->xy);
 
					InvalidateTownForRoadTile();
 
				}
 
			} break;
 

	
 
			case TSEW_MANYRANDOMTOWNS:
 
				this->HandleButtonClick(TSEW_MANYRANDOMTOWNS);
 

	
 
				_generating_world = true;
 
				UpdateNearestTownForRoadTiles(true);
 
				if (!GenerateTowns()) {
 
					ShowErrorMessage(STR_NO_SPACE_FOR_TOWN, STR_CANNOT_GENERATE_TOWN, 0, 0);
 
				} else {
 
					InvalidateTownForRoadTile();
 
				}
 
				UpdateNearestTownForRoadTiles(false);
 
				_generating_world = false;
 
				break;
 

	
 
			case TSEW_SMALLTOWN: case TSEW_MEDIUMTOWN: case TSEW_LARGETOWN: case TSEW_CITY:
 
				this->RaiseWidget(_scengen_town_size + TSEW_SMALLTOWN);
 
				_scengen_town_size = widget - TSEW_SMALLTOWN;
0 comments (0 inline, 0 general)