Index: src/core/math_func.hpp =================================================================== --- src/core/math_func.hpp (Revision 23438) +++ src/core/math_func.hpp (Arbeitskopie) @@ -348,4 +348,18 @@ uint32 IntSqrt(uint32 num); +/** + * Computes the smallest nonnegative number r with a = q*b + r. + * (remainder of integer division with rounding towards -infinity) + * We can't rely on % giving sane results for negative a. + * @param a Numerator + * @param b Denominator + * @return Remainder + */ +static FORCEINLINE uint Mod(int a, uint b) +{ + if (a >= 0) return ((uint)a)%b; + return b-(((uint)(-a-1))%b)-1; +} + #endif /* MATH_FUNC_HPP */ Index: src/lang/english.txt =================================================================== --- src/lang/english.txt (Revision 23438) +++ src/lang/english.txt (Arbeitskopie) @@ -1298,11 +1298,11 @@ STR_CONFIG_SETTING_CYCLE_SIGNAL_ALL :All STR_CONFIG_SETTING_TOWN_LAYOUT :{LTBLUE}Road layout for new towns: {ORANGE}{STRING1} -STR_CONFIG_SETTING_TOWN_LAYOUT_DEFAULT :original -STR_CONFIG_SETTING_TOWN_LAYOUT_BETTER_ROADS :better roads -STR_CONFIG_SETTING_TOWN_LAYOUT_2X2_GRID :2x2 grid -STR_CONFIG_SETTING_TOWN_LAYOUT_3X3_GRID :3x3 grid +STR_CONFIG_SETTING_TOWN_LAYOUT_DEFAULT :natural +STR_CONFIG_SETTING_TOWN_LAYOUT_GRID :grid STR_CONFIG_SETTING_TOWN_LAYOUT_RANDOM :random +STR_CONFIG_SETTING_MIN_TOWN_SPACING :{LTBLUE}Smallest minimum distance beetween parallel roads: {ORANGE}{STRING1} +STR_CONFIG_SETTING_MAX_TOWN_SPACING :{LTBLUE}Largest minimum distance beetween parallel roads: {ORANGE}{STRING1} STR_CONFIG_SETTING_ALLOW_TOWN_ROADS :{LTBLUE}Towns are allowed to build roads: {ORANGE}{STRING1} STR_CONFIG_SETTING_ALLOW_TOWN_LEVEL_CROSSINGS :{LTBLUE}Towns are allowed to build level crossings: {ORANGE}{STRING1} STR_CONFIG_SETTING_NOISE_LEVEL :{LTBLUE}Allow town controlled noise level for airports: {ORANGE}{STRING} Index: src/table/settings.ini =================================================================== --- src/table/settings.ini (Revision 23438) +++ src/table/settings.ini (Arbeitskopie) @@ -475,7 +475,7 @@ type = SLE_UINT8 from = 59 guiflags = SGF_MULTISTRING -def = TL_ORIGINAL +def = TL_NATURAL min = TL_BEGIN max = NUM_TLS - 1 interval = 1 @@ -483,6 +483,29 @@ strval = STR_CONFIG_SETTING_TOWN_LAYOUT_DEFAULT proc = TownFoundingChanged +[SDT_VAR] +base = GameSettings +var = economy.town_min_spacing +type = SLE_UINT8 +from = 167 +def = 2 +min = 1 +max = MAX_TOWN_SPACING +interval = 1 +str = STR_CONFIG_SETTING_MIN_TOWN_SPACING + +[SDT_VAR] +base = GameSettings +var = economy.town_max_spacing +type = SLE_UINT8 +from = 167 +def = 2 +min = 1 +max = MAX_TOWN_SPACING +interval = 1 +str = STR_CONFIG_SETTING_MAX_TOWN_SPACING +#proc = check max >= min + [SDT_BOOL] base = GameSettings var = economy.allow_town_roads Index: src/genworld.cpp =================================================================== --- src/genworld.cpp (Revision 23438) +++ src/genworld.cpp (Arbeitskopie) @@ -129,7 +129,7 @@ /* only generate towns, tree and industries in newgame mode. */ if (_game_mode != GM_EDITOR) { - if (!GenerateTowns(_settings_game.economy.town_layout)) { + if (!GenerateTowns(_settings_game.economy.town_layout, TS_RANDOM)) { _cur_company.Restore(); HandleGeneratingWorldAbortion(); return; Index: src/settings_gui.cpp =================================================================== --- src/settings_gui.cpp (Revision 23438) +++ src/settings_gui.cpp (Arbeitskopie) @@ -1459,6 +1459,8 @@ SettingEntry("economy.fund_roads"), SettingEntry("economy.fund_buildings"), SettingEntry("economy.town_layout"), + SettingEntry("economy.town_min_spacing"), + SettingEntry("economy.town_max_spacing"), SettingEntry("economy.allow_town_roads"), SettingEntry("economy.allow_town_level_crossings"), SettingEntry("economy.found_town"), Index: src/settings_type.h =================================================================== --- src/settings_type.h (Revision 23438) +++ src/settings_type.h (Arbeitskopie) @@ -411,6 +411,8 @@ uint8 larger_towns; ///< the number of cities to build. These start off larger and grow twice as fast uint8 initial_city_size; ///< multiplier for the initial size of the cities compared to towns TownLayoutByte town_layout; ///< select town layout, @see TownLayout + TownSpacing town_min_spacing; ///< smallest minimal distance between parallel roads for towns + TownSpacing town_max_spacing; ///< largest minimal distance between parallel roads for towns bool allow_town_roads; ///< towns are allowed to build roads (always allowed when generating world / in SE) TownFoundingByte found_town; ///< town founding, @see TownFounding bool station_noise_level; ///< build new airports when the town noise level is still within accepted limits Index: src/town_cmd.cpp =================================================================== --- src/town_cmd.cpp (Revision 23438) +++ src/town_cmd.cpp (Arbeitskopie) @@ -126,8 +126,16 @@ /** * Assigns town layout. If Random, generates one based on TileHash. */ -void Town::InitializeLayout(TownLayout layout) +void Town::InitializeLayout(TownLayout layout, TownSpacing spacing) { + if (spacing == TS_RANDOM) { + TownSpacing min_spacing = _settings_game.economy.town_min_spacing; + TownSpacing max_spacing = max(_settings_game.economy.town_max_spacing, _settings_game.economy.town_min_spacing); + this->spacing = (TileHash(TileX(this->xy), TileY(this->xy)) / (NUM_TLS - 1)) % (max_spacing - min_spacing + 1) + min_spacing; + } else { + this->spacing = spacing; + } + if (layout != TL_RANDOM) { this->layout = layout; return; @@ -180,7 +188,7 @@ }; static bool BuildTownHouse(Town *t, TileIndex tile); -static Town *CreateRandomTown(uint attempts, uint32 townnameparts, TownSize size, bool city, TownLayout layout); +static Town *CreateRandomTown(uint attempts, uint32 townnameparts, TownSize size, bool city, TownLayout layout, TownSpacing spacing); static void TownDrawHouseLift(const TileInfo *ti) { @@ -861,7 +869,7 @@ } Slope cur_slope = _settings_game.construction.build_on_slopes ? GetFoundationSlope(tile) : GetTileSlope(tile); - bool ret = !IsNeighborRoadTile(tile, dir, t->layout == TL_ORIGINAL ? 1 : 2); + bool ret = !IsNeighborRoadTile(tile, dir, t->spacing); if (cur_slope == SLOPE_FLAT) return ret; /* If the tile is not a slope in the right direction, then @@ -928,15 +936,10 @@ switch (t->layout) { default: NOT_REACHED(); - case TL_2X2_GRID: - if ((grid_pos.x % 3) == 0) rcmd |= ROAD_Y; - if ((grid_pos.y % 3) == 0) rcmd |= ROAD_X; + case TL_GRID: + if ((grid_pos.x % (t->spacing + 1)) == 0) rcmd |= ROAD_Y; + if ((grid_pos.y % (t->spacing + 1)) == 0) rcmd |= ROAD_X; break; - - case TL_3X3_GRID: - if ((grid_pos.x % 4) == 0) rcmd |= ROAD_Y; - if ((grid_pos.y % 4) == 0) rcmd |= ROAD_X; - break; } /* Optimise only X-junctions */ @@ -1134,14 +1137,12 @@ switch (t1->layout) { default: NOT_REACHED(); - case TL_3X3_GRID: - case TL_2X2_GRID: + case TL_GRID: rcmd = GetTownRoadGridElement(t1, tile, target_dir); if (rcmd == ROAD_NONE) return; break; - case TL_BETTER_ROADS: - case TL_ORIGINAL: + case TL_NATURAL: if (!IsRoadAllowedHere(t1, tile, target_dir)) return; DiagDirection source_dir = ReverseDiagDir(target_dir); @@ -1181,13 +1182,11 @@ switch (t1->layout) { default: NOT_REACHED(); - case TL_3X3_GRID: - case TL_2X2_GRID: + case TL_GRID: rcmd = GetTownRoadGridElement(t1, tile, target_dir); break; - case TL_BETTER_ROADS: - case TL_ORIGINAL: + case TL_NATURAL: rcmd = DiagDirToRoadBits(ReverseDiagDir(target_dir)); break; } @@ -1217,23 +1216,16 @@ if (!IsValidTile(house_tile)) return; if (_settings_game.economy.allow_town_roads || _generating_world) { + if (t1->spacing > 2) GrowTownWithExtraHouse(t1, TileAddByDiagDir(house_tile, target_dir)); switch (t1->layout) { default: NOT_REACHED(); - case TL_3X3_GRID: // Use 2x2 grid afterwards! - GrowTownWithExtraHouse(t1, TileAddByDiagDir(house_tile, target_dir)); - /* FALL THROUGH */ - - case TL_2X2_GRID: + case TL_GRID: rcmd = GetTownRoadGridElement(t1, house_tile, target_dir); allow_house = (rcmd == ROAD_NONE); break; - case TL_BETTER_ROADS: // Use original afterwards! - GrowTownWithExtraHouse(t1, TileAddByDiagDir(house_tile, target_dir)); - /* FALL THROUGH */ - - case TL_ORIGINAL: + case TL_NATURAL: /* Allow a house at the edge. 60% chance or * always ok if no road allowed. */ rcmd = DiagDirToRoadBits(target_dir); @@ -1291,21 +1283,19 @@ assert(tile < MapSize()); /* Number of times to search. - * Better roads, 2X2 and 3X3 grid grow quite fast so we give + * spaced out and grid towns grow quite fast so we give * them a little handicap. */ switch (t->layout) { - case TL_BETTER_ROADS: - _grow_town_result = 10 + t->num_houses * 2 / 9; + case TL_NATURAL: + _grow_town_result = 10 + t->num_houses * ((t->spacing > 1)?2:4) / 9; break; - case TL_3X3_GRID: - case TL_2X2_GRID: + case TL_GRID: _grow_town_result = 10 + t->num_houses * 1 / 9; break; default: - _grow_town_result = 10 + t->num_houses * 4 / 9; - break; + NOT_REACHED(); } do { @@ -1485,7 +1475,7 @@ * @param layout the (road) layout of the town * @param manual was the town placed manually? */ -static void DoCreateTown(Town *t, TileIndex tile, uint32 townnameparts, TownSize size, bool city, TownLayout layout, bool manual) +static void DoCreateTown(Town *t, TileIndex tile, uint32 townnameparts, TownSize size, bool city, TownLayout layout, TownSpacing spacing, bool manual) { t->xy = tile; t->num_houses = 0; @@ -1532,7 +1522,7 @@ t->UpdateVirtCoord(); InvalidateWindowData(WC_TOWN_DIRECTORY, 0, 0); - t->InitializeLayout(layout); + t->InitializeLayout(layout, spacing); t->larger_town = city; @@ -1615,17 +1605,23 @@ TownLayout layout = Extract(p1); TownNameParams par(_settings_game.game_creation.town_name); bool random = HasBit(p1, 6); + TownSpacing spacing = Extract(p1); uint32 townnameparts = p2; if (size >= TSZ_END) return CMD_ERROR; if (layout >= NUM_TLS) return CMD_ERROR; + if (spacing > MAX_TOWN_SPACING) return CMD_ERROR; /* Some things are allowed only in the scenario editor */ if (_game_mode != GM_EDITOR) { if (_settings_game.economy.found_town == TF_FORBIDDEN) return CMD_ERROR; if (size == TSZ_LARGE) return CMD_ERROR; if (random) return CMD_ERROR; - if (_settings_game.economy.found_town != TF_CUSTOM_LAYOUT && layout != _settings_game.economy.town_layout) { + if (_settings_game.economy.found_town != TF_CUSTOM_LAYOUT && + (layout != _settings_game.economy.town_layout || + spacing < _settings_game.economy.town_min_spacing || + spacing > _settings_game.economy.town_max_spacing + )) { return CMD_ERROR; } } @@ -1667,7 +1663,7 @@ UpdateNearestTownForRoadTiles(true); Town *t; if (random) { - t = CreateRandomTown(20, townnameparts, size, city, layout); + t = CreateRandomTown(20, townnameparts, size, city, layout, spacing); if (t == NULL) { cost = CommandCost(STR_ERROR_NO_SPACE_FOR_TOWN); } else { @@ -1675,7 +1671,7 @@ } } else { t = new Town(tile); - DoCreateTown(t, tile, townnameparts, size, city, layout, true); + DoCreateTown(t, tile, townnameparts, size, city, layout, spacing, true); } UpdateNearestTownForRoadTiles(false); _generating_world = false; @@ -1712,11 +1708,10 @@ * @param layout which town layout algo is in effect * @return the adjusted tile */ -static TileIndex AlignTileToGrid(TileIndex tile, TownLayout layout) +static TileIndex AlignTileToGrid(TileIndex tile, TownLayout layout, TownSpacing spacing) { switch (layout) { - case TL_2X2_GRID: return TileXY(TileX(tile) - TileX(tile) % 3, TileY(tile) - TileY(tile) % 3); - case TL_3X3_GRID: return TileXY(TileX(tile) & ~3, TileY(tile) & ~3); + case TL_GRID: return TileXY(TileX(tile) - TileX(tile) % (spacing + 1), TileY(tile) - TileY(tile) % (spacing + 1)); default: return tile; } } @@ -1730,11 +1725,10 @@ * @param layout which town layout algo is in effect * @return true if the tile is in the correct location */ -static bool IsTileAlignedToGrid(TileIndex tile, TownLayout layout) +static bool IsTileAlignedToGrid(TileIndex tile, TownLayout layout, TownSpacing spacing) { switch (layout) { - case TL_2X2_GRID: return TileX(tile) % 3 == 0 && TileY(tile) % 3 == 0; - case TL_3X3_GRID: return TileX(tile) % 4 == 0 && TileY(tile) % 4 == 0; + case TL_GRID: return TileX(tile) % (spacing + 1) == 0 && TileY(tile) % (spacing + 1) == 0; default: return true; } } @@ -1746,6 +1740,7 @@ TileIndex tile; ///< holds the tile that was found uint max_dist; ///< holds the distance that tile is from the water TownLayout layout; ///< tells us what kind of town we're building + TownSpacing spacing; ///layout) && + IsTileAlignedToGrid(tile, sp->layout, sp->spacing) && dist > sp->max_dist) { sp->tile = tile; sp->max_dist = dist; @@ -1803,9 +1798,9 @@ * @param layout the road layout to search for * @return tile that was found */ -static TileIndex FindNearestGoodCoastalTownSpot(TileIndex tile, TownLayout layout) +static TileIndex FindNearestGoodCoastalTownSpot(TileIndex tile, TownLayout layout, TownSpacing spacing) { - SpotData sp = { INVALID_TILE, 0, layout }; + SpotData sp = { INVALID_TILE, 0, layout, spacing }; TileIndex coast = tile; if (CircularTileSearch(&coast, 40, FindNearestEmptyLand, NULL)) { @@ -1817,18 +1812,18 @@ return INVALID_TILE; } -static Town *CreateRandomTown(uint attempts, uint32 townnameparts, TownSize size, bool city, TownLayout layout) +static Town *CreateRandomTown(uint attempts, uint32 townnameparts, TownSize size, bool city, TownLayout layout, TownSpacing spacing) { if (!Town::CanAllocateItem()) return NULL; do { /* Generate a tile index not too close from the edge */ - TileIndex tile = AlignTileToGrid(RandomTile(), layout); + TileIndex tile = AlignTileToGrid(RandomTile(), layout, spacing); /* if we tried to place the town on water, slide it over onto * the nearest likely-looking spot */ if (IsTileType(tile, MP_WATER)) { - tile = FindNearestGoodCoastalTownSpot(tile, layout); + tile = FindNearestGoodCoastalTownSpot(tile, layout, spacing); if (tile == INVALID_TILE) continue; } @@ -1838,7 +1833,7 @@ /* Allocate a town struct */ Town *t = new Town(tile); - DoCreateTown(t, tile, townnameparts, size, city, layout, false); + DoCreateTown(t, tile, townnameparts, size, city, layout, spacing, false); /* if the population is still 0 at the point, then the * placement is so bad it couldn't grow at all */ @@ -1864,7 +1859,7 @@ * @param layout which towns will be set to, when created * @return true if towns have been successfully created */ -bool GenerateTowns(TownLayout layout) +bool GenerateTowns(TownLayout layout, TownSpacing spacing) { uint current_number = 0; uint difficulty = (_game_mode != GM_EDITOR) ? _settings_game.difficulty.number_towns : 0; @@ -1882,7 +1877,7 @@ /* Get a unique name for the town. */ if (!GenerateTownName(&townnameparts)) continue; /* try 20 times to create a random-sized town for the first loop. */ - if (CreateRandomTown(20, townnameparts, TSZ_RANDOM, city, layout) != NULL) current_number++; // If creation was successful, raise a flag. + if (CreateRandomTown(20, townnameparts, TSZ_RANDOM, city, layout, spacing) != NULL) current_number++; // If creation was successful, raise a flag. } while (--total); if (current_number != 0) return true; @@ -1890,7 +1885,7 @@ /* If current_number is still zero at this point, it means that not a single town has been created. * So give it a last try, but now more aggressive */ if (GenerateTownName(&townnameparts) && - CreateRandomTown(10000, townnameparts, TSZ_RANDOM, _settings_game.economy.larger_towns != 0, layout) != NULL) { + CreateRandomTown(10000, townnameparts, TSZ_RANDOM, _settings_game.economy.larger_towns != 0, layout, spacing) != NULL) { return true; } @@ -2052,14 +2047,10 @@ TileIndexDiffC grid_pos = TileIndexToTileIndexDiffC(t->xy, tile); switch (t->layout) { - case TL_2X2_GRID: - if ((grid_pos.x % 3) == 0 || (grid_pos.y % 3) == 0) return false; + case TL_GRID: + if ((grid_pos.x % (t->spacing + 1)) == 0 || (grid_pos.y % (t->spacing + 1)) == 0) return false; break; - case TL_3X3_GRID: - if ((grid_pos.x % 4) == 0 || (grid_pos.y % 4) == 0) return false; - break; - default: break; } @@ -2084,17 +2075,12 @@ TileIndexDiffC grid_pos = TileIndexToTileIndexDiffC(t->xy, tile); switch (t->layout) { - case TL_2X2_GRID: - grid_pos.x %= 3; - grid_pos.y %= 3; - if ((grid_pos.x != 2 && grid_pos.x != -1) || - (grid_pos.y != 2 && grid_pos.y != -1)) return false; + case TL_GRID: + grid_pos.x = Mod(grid_pos.x, t->spacing + 1); + grid_pos.y = Mod(grid_pos.y, t->spacing + 1); + if ((grid_pos.x <= 2) || (grid_pos.y < 2)) return false; break; - case TL_3X3_GRID: - if ((grid_pos.x & 3) < 2 || (grid_pos.y & 3) < 2) return false; - break; - default: break; } Index: src/town_gui.cpp =================================================================== --- src/town_gui.cpp (Revision 23438) +++ src/town_gui.cpp (Arbeitskopie) @@ -1110,7 +1110,7 @@ _generating_world = true; UpdateNearestTownForRoadTiles(true); - if (!GenerateTowns(this->town_layout)) { + if (!GenerateTowns(this->town_layout, TS_RANDOM)) { //TODO: allow selecting spacing ShowErrorMessage(STR_ERROR_CAN_T_GENERATE_TOWN, STR_ERROR_NO_SPACE_FOR_TOWN, WL_INFO); } UpdateNearestTownForRoadTiles(false); Index: src/town.h =================================================================== --- src/town.h (Revision 23438) +++ src/town.h (Arbeitskopie) @@ -94,6 +94,7 @@ bool larger_town; ///< if this is a larger town and should grow more quickly TownLayoutByte layout; ///< town specific road layout + TownSpacing spacing; ///< minimum distance between parallel roads std::list psa_list; @@ -112,7 +113,7 @@ /** Destroy the town. */ ~Town(); - void InitializeLayout(TownLayout layout); + void InitializeLayout(TownLayout layout, TownSpacing spacing); /** * Calculate the max town noise. @@ -192,7 +193,7 @@ HouseZonesBits GetTownRadiusGroup(const Town *t, TileIndex tile); void SetTownRatingTestMode(bool mode); uint GetMaskOfTownActions(int *nump, CompanyID cid, const Town *t); -bool GenerateTowns(TownLayout layout); +bool GenerateTowns(TownLayout layout, TownSpacing spacing); const CargoSpec *FindFirstCargoWithTownEffect(TownEffect effect); Index: src/town_type.h =================================================================== --- src/town_type.h (Revision 23438) +++ src/town_type.h (Arbeitskopie) @@ -81,15 +81,17 @@ */ enum TownLayout { TL_BEGIN = 0, - TL_ORIGINAL = 0, ///< Original algorithm (min. 1 distance between roads) - TL_BETTER_ROADS, ///< Extended original algorithm (min. 2 distance between roads) - TL_2X2_GRID, ///< Geometric 2x2 grid algorithm - TL_3X3_GRID, ///< Geometric 3x3 grid algorithm + TL_NATURAL = 0, ///< Original algorithm + TL_GRID, ///< Geometric grid algorithm TL_RANDOM, ///< Random town layout NUM_TLS, ///< Number of town layouts }; +typedef byte TownSpacing; +static const TownSpacing TS_RANDOM = 0; +static const TownSpacing MAX_TOWN_SPACING = 6; + template <> struct EnumPropsT : MakeEnumPropsT {}; /** It needs to be 8bits, because we save and load it as such */ typedef SimpleTinyEnumT TownLayoutByte; // typedefing-enumification of TownLayout Index: src/saveload/afterload.cpp =================================================================== --- src/saveload/afterload.cpp (Revision 23438) +++ src/saveload/afterload.cpp (Arbeitskopie) @@ -1960,7 +1960,7 @@ /* allow_town_roads is added, set it if town_layout wasn't TL_NO_ROADS */ if (_settings_game.economy.town_layout == 0) { // was TL_NO_ROADS _settings_game.economy.allow_town_roads = false; - _settings_game.economy.town_layout = TL_BETTER_ROADS; + _settings_game.economy.town_layout = TL_NATURAL; } else { _settings_game.economy.allow_town_roads = true; _settings_game.economy.town_layout = _settings_game.economy.town_layout - 1; @@ -2713,6 +2713,32 @@ } } + if (IsSavegameVersionBefore(167)) { + /* Town spacing parameter has been added */ + Town *t; + FOR_ALL_TOWNS(t) { + switch (t->layout) { + default: NOT_REACHED(); + case 0: /* TL_ORIGINAL */ + t->layout = TL_NATURAL; + t->spacing = 1; + break; + case 1: /* TL_BETTER_ROADS */ + t->layout = TL_NATURAL; + t->spacing = 2; + break; + case 2: /* TL_2x2_GRID */ + t->layout = TL_GRID; + t->spacing = 2; + break; + case 3: /* TL_3x3_GRID */ + t->layout = TL_GRID; + t->spacing = 3; + break; + } + } + } + /* Road stops is 'only' updating some caches */ AfterLoadRoadStops(); AfterLoadLabelMaps(); Index: src/saveload/saveload.cpp =================================================================== --- src/saveload/saveload.cpp (Revision 23438) +++ src/saveload/saveload.cpp (Arbeitskopie) @@ -231,8 +231,9 @@ * 164 23290 * 165 23304 * 166 23415 + * 167 town road speard */ -extern const uint16 SAVEGAME_VERSION = 166; ///< Current savegame version of OpenTTD. +extern const uint16 SAVEGAME_VERSION = 167; ///< Current savegame version of OpenTTD. SavegameType _savegame_type; ///< type of savegame we are loading