Skip to content

Commit

Permalink
Feature: Permanent rivers
Browse files Browse the repository at this point in the history
- Rivers are indestructible, unless cheating or in the scenario editor.
- Added a game setting that can turn on or off this feature.
- Savegame conversion.

Other changes:
- Demolishing a canal that was built on rivers will restore the river, regardless of setting value.
- Can no longer use terraform tool to remove rivers, must demolish them first, regardless of setting value.
  • Loading branch information
SamuXarick committed Aug 3, 2021
1 parent 1838726 commit 150d825
Show file tree
Hide file tree
Showing 13 changed files with 152 additions and 26 deletions.
4 changes: 4 additions & 0 deletions docs/landscape.html
Expand Up @@ -998,6 +998,7 @@ <h3><a name="Landscape">Landscape</a></h3>
<li>m7: animation frame (railway stations/waypoints, airports)</li>
<li>m8 bits 11..6: <a href="#TramType">Tramtype</a></li>
<li>m8 bits 5..0: <a href="#TrackType">track type</a> for railway stations/waypoints</li>
<li>m8 bit 15: set if a river was originally present under water based station tiles when current water class is canal (dock, buoy, oilrig)</li>
</ul>
</td>
</tr>
Expand Down Expand Up @@ -1108,6 +1109,7 @@ <h3><a name="Landscape">Landscape</a></h3>
</tr>
</table>
</li>
<li>m8 bit 15: set if a river was originally present at the tile when current water class is canal (canal, ship depot, lock)</li>
</ul>
</td>
</tr>
Expand Down Expand Up @@ -1450,6 +1452,7 @@ <h3><a name="Landscape">Landscape</a></h3>
<li>m6 bits 5..3: random triggers (NewGRF)</li>
<li>m6 bit 2: bit 8 of type (see m5)</li>
<li>m7: animation frame</li>
<li>m8 bit 15: set if a river was originally present at the tile when current water class is canal (oilrig)
</ul>
</td>
</tr>
Expand Down Expand Up @@ -1622,6 +1625,7 @@ <h3><a name="Landscape">Landscape</a></h3>
<li>m3: random bits</li>
<li>m5: index into the array of objects, bits 16 to 23 (lower bits in m2)</li>
<li>m7: animation counter</li>
<li>m8 bit 15: set if a river was originally present at the tile when current water class is canal</li>
</ul>
</td>
</tr>
Expand Down
35 changes: 20 additions & 15 deletions docs/landscape_grid.html
Expand Up @@ -79,8 +79,8 @@ <h3 style="font-weight: bold;">Landscape</h3>
<tr>
<td rowspan="2">0</td>
<td class="caption">ground</td>
<td class="bits" rowspan=27><span class="used" title="Tile type">XXXX</span> <span class="used" title="Presence and direction of bridge above">XX</span> <span class="used" title="Tropic Zone: only meaningfull in tropic climate. It contains the definition of the available zones">XX</span></td>
<td class="bits" rowspan=27><span class="used" title="Tile height">XXXX XXXX</span></td>
<td class="bits" rowspan=28><span class="used" title="Tile type">XXXX</span> <span class="used" title="Presence and direction of bridge above">XX</span> <span class="used" title="Tropic Zone: only meaningfull in tropic climate. It contains the definition of the available zones">XX</span></td>
<td class="bits" rowspan=28><span class="used" title="Tile height">XXXX XXXX</span></td>
<td class="bits" rowspan=2><span class="free">OOO</span><span class="usable" title="Owner (always OWNER_NONE)">1 OOOO</span></td>
<td class="bits"><span class="free">OOOO OOOO OOOO OOOO</span></td>
<td class="bits"><span class="used" title="Type of hedge on NE border">XXX</span> <span class="used" title="Snow presence">X</span><span class="free">OOOO</span></td>
Expand Down Expand Up @@ -211,14 +211,15 @@ <h3 style="font-weight: bold;">Landscape</h3>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="used" title="Graphics index">XXXX XXXX</span></td>
<td class="bits"><span class="used" title="Animation frame">XXXX XXXX</span></td>
<td class="bits" rowspan=4><span class="free">OOOO OOOO OOOO OOOO</span></td>
<td class="bits"><span class="free">OOOO OOOO OOOO OOOO</span></td>
</tr>
<tr>
<td class="caption">dock</td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="usable" title="Graphics index">OOOO </span><span class="usable" title="Graphics index">O</span><span class="used" title="Graphics index">XXX</span></td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits" rowspan=3><span class="used" title="Canal on river presence">X</span><span class="free">OOO OOOO OOOO OOOO</span></td>
</tr>
<tr>
<td class="caption">buoy</td>
Expand All @@ -235,21 +236,25 @@ <h3 style="font-weight: bold;">Landscape</h3>
<td class="bits"><span class="free">OOOO OOOO</span></td>
</tr>
<tr>
<td rowspan=4>6</td>
<td rowspan=5>6</td>
<td class="caption">sea, shore</td>
<td class="bits" rowspan=4><span class="used" title="Ship docking tile status">X</span> <span class="used" title="Water class">XX</span> <span class="used" title="Owner">XXXXX</span></td>
<td class="bits" rowspan=3><span class="free">OOOO OOOO OOOO OOOO</span></td>
<td class="bits" rowspan=4><span class="free">OOOO OOOO</span></td>
<td class="bits" rowspan=5><span class="used" title="Ship docking tile status">X</span> <span class="used" title="Water class">XX</span> <span class="used" title="Owner">XXXXX</span></td>
<td class="bits" rowspan=4><span class="free">OOOO OOOO OOOO OOOO</span></td>
<td class="bits" rowspan=5><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="used" title="Water tile type: coast, clear, lock, depot">O<span class="usable">OO</span>O</span> <span class="free">OOO</span><span class="used" title="Sea shore flag">X</span></td>
<td class="bits" rowspan=4><span class="free">OOOO OOOO</span></td>
<td class="bits" rowspan=4><span class="free">OOOO OOOO</td>
<td class="bits" rowspan=4><span class="free">OOOO OOOO OOOO OOOO</span></td>
<td class="bits" rowspan=5><span class="free">OOOO OOOO</span></td>
<td class="bits" rowspan=5><span class="free">OOOO OOOO</td>
<td class="bits" rowspan=2><span class="free">OOOO OOOO OOOO OOOO</span></td>
</tr>
<tr>
<td class="caption">river</td>
<td class="bits" rowspan=2><span class="used" title="Canal/river random bits">XXXX XXXX</span></td>
<td class="bits" rowspan=2><span class="used" title="Water tile type: coast, clear, lock, depot">O<span class="usable">OO</span>O</span> <span class="free">OOOO</span></td>
</tr>
<tr>
<td class="caption">canal, river</td>
<td class="bits"><span class="used" title="Canal/river random bits">XXXX XXXX</span></td>
<td class="bits"><span class="used" title="Water tile type: coast, clear, lock, depot">O<span class="usable">OO</span>O</span> <span class="free">OOOO</span></td>
<td class="caption">canal</td>
<td class="bits" rowspan=3><span class="used" title="Canal on river presence">X</span><span class="free">OOO OOOO OOOO OOOO</span></td>
</tr>
<tr>
<td class="caption">lock</td>
Expand All @@ -272,7 +277,7 @@ <h3 style="font-weight: bold;">Landscape</h3>
<td class="bits" rowspan=2><span class="used" title="Industry graphics ID (m5 + m6[2])">XXXX XXXX</span></td>
<td class="bits" rowspan=2><span class="free">OO</span><span class="used" title="Random triggers (NewGRF)">XXX</span> <span class="used" title="Industry graphics ID (m5 + m6[2])">X</span><span class="free">OO</span></td>
<td class="bits" rowspan=2><span class="used" title="Animation frame">XXXX XXXX</span></td>
<td class="bits" rowspan=2><span class="free">OOOO OOOO OOOO OOOO</span></td>
<td class="bits" rowspan=2><span class="used" title="Canal on river presence">X</span><span class="free">OOO OOOO OOOO OOOO</span></td>
</tr>
<tr>
<td class="caption">industry under construction</td>
Expand Down Expand Up @@ -305,7 +310,7 @@ <h3 style="font-weight: bold;">Landscape</h3>
<td class="bits"><span class="pool" title="Object index on pool (m2 + m5)">XXXX XXXX</span></td>
<td class="bits"><span class="free">OOOO OOOO</span></td>
<td class="bits"><span class="used" title="Animation counter">XXXX XXXX</span></td>
<td class="bits" rowspan=1><span class="free">OOOO OOOO OOOO OOOO</span></td>
<td class="bits"><span class="used" title="Canal on river presence">X</span><span class="free">OOO OOOO OOOO OOOO</span></td>
</tr>
<tr>
<td colspan=2 class="caption">bits</td>
Expand Down
2 changes: 2 additions & 0 deletions src/industry_cmd.cpp
Expand Up @@ -1882,10 +1882,12 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
i->location.Add(cur_tile);

WaterClass wc = (IsWaterTile(cur_tile) ? GetWaterClass(cur_tile) : WATER_CLASS_INVALID);
bool river = HasTileCanalOnRiver(cur_tile);

DoCommand(cur_tile, 0, 0, DC_EXEC | DC_NO_TEST_TOWN_RATING | DC_NO_MODIFY_TOWN_RATING, CMD_LANDSCAPE_CLEAR);

MakeIndustry(cur_tile, i->index, it.gfx, Random(), wc);
if (river) SetCanalOnRiver(cur_tile);

if (_generating_world) {
SetIndustryConstructionCounter(cur_tile, 3);
Expand Down
4 changes: 4 additions & 0 deletions src/lang/english.txt
Expand Up @@ -1231,6 +1231,8 @@ STR_CONFIG_SETTING_SERVE_NEUTRAL_INDUSTRIES :Company station
STR_CONFIG_SETTING_SERVE_NEUTRAL_INDUSTRIES_HELPTEXT :When enabled, industries with attached stations (such as Oil Rigs) may also be served by company owned stations built nearby. When disabled, these industries may only be served by their attached stations. Any nearby company stations won't be able to serve them, nor will the attached station serve anything else other than the industry
STR_CONFIG_SETTING_EXTRADYNAMITE :Allow removal of more town-owned roads, bridges and tunnels: {STRING2}
STR_CONFIG_SETTING_EXTRADYNAMITE_HELPTEXT :Make it easier to remove town-owned infrastructure and buildings
STR_CONFIG_SETTING_DYNAMITE_RIVER :Allow removal of rivers: {STRING2}
STR_CONFIG_SETTING_DYNAMITE_RIVER_HELPTEXT :If disabled, rivers are indestructible and become a permanent part of the landscape
STR_CONFIG_SETTING_TRAIN_LENGTH :Maximum length of trains: {STRING2}
STR_CONFIG_SETTING_TRAIN_LENGTH_HELPTEXT :Set the maximum length of trains
STR_CONFIG_SETTING_TILE_LENGTH :{COMMA} tile{P 0 "" s}
Expand Down Expand Up @@ -2803,6 +2805,7 @@ STR_LAI_STATION_DESCRIPTION_WAYPOINT :Waypoint

STR_LAI_WATER_DESCRIPTION_WATER :Water
STR_LAI_WATER_DESCRIPTION_CANAL :Canal
STR_LAI_WATER_DESCRIPTION_CANALISED_RIVER :Canalised river
STR_LAI_WATER_DESCRIPTION_LOCK :Lock
STR_LAI_WATER_DESCRIPTION_RIVER :River
STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK :Coast or riverbank
Expand Down Expand Up @@ -4626,6 +4629,7 @@ STR_ERROR_CAN_T_BUILD_ON_CANAL :{WHITE}... can'
STR_ERROR_CAN_T_BUILD_ON_RIVER :{WHITE}... can't build on river
STR_ERROR_MUST_DEMOLISH_CANAL_FIRST :{WHITE}Must demolish canal first
STR_ERROR_CAN_T_BUILD_AQUEDUCT_HERE :{WHITE}Can't build aqueduct here...
STR_ERROR_MUST_CLEAR_RIVER_FIRST :{WHITE}Must clear river first

# Tree related errors
STR_ERROR_TREE_ALREADY_HERE :{WHITE}... tree already here
Expand Down
5 changes: 5 additions & 0 deletions src/saveload/afterload.cpp
Expand Up @@ -3147,6 +3147,11 @@ bool AfterLoadGame()
}
}

if (IsSavegameVersionBefore(SLV_PERMANENT_RIVERS)) {
/* When loading an older savegame version, treat 'dynamite river' as enabled. */
_settings_game.construction.dynamite_river = true;
}

/* Compute station catchment areas. This is needed here in case UpdateStationAcceptance is called below. */
Station::RecomputeCatchmentForAll();

Expand Down
1 change: 1 addition & 0 deletions src/saveload/saveload.h
Expand Up @@ -337,6 +337,7 @@ enum SaveLoadVersion : uint16 {

SLV_TABLE_CHUNKS, ///< 295 PR#9322 Introduction of CH_TABLE and CH_SPARSE_TABLE.
SLV_SCRIPT_INT64, ///< 296 PR#9415 SQInteger is 64bit but was saved as 32bit.
SLV_PERMANENT_RIVERS, ///< 297 PR#8461 Permanent rivers.

SL_MAX_VERSION, ///< Highest possible saveload version
};
Expand Down
1 change: 1 addition & 0 deletions src/settings_gui.cpp
Expand Up @@ -1730,6 +1730,7 @@ static SettingsContainer &GetSettingsTree()
limitations->Add(new SettingEntry("construction.command_pause_level"));
limitations->Add(new SettingEntry("construction.autoslope"));
limitations->Add(new SettingEntry("construction.extra_dynamite"));
limitations->Add(new SettingEntry("construction.dynamite_river"));
limitations->Add(new SettingEntry("construction.map_height_limit"));
limitations->Add(new SettingEntry("construction.max_bridge_length"));
limitations->Add(new SettingEntry("construction.max_bridge_height"));
Expand Down
1 change: 1 addition & 0 deletions src/settings_type.h
Expand Up @@ -341,6 +341,7 @@ struct ConstructionSettings {
bool extra_dynamite; ///< extra dynamite
bool road_stop_on_town_road; ///< allow building of drive-through road stops on town owned roads
bool road_stop_on_competitor_road; ///< allow building of drive-through road stops on roads owned by competitors
bool dynamite_river; ///< allow removal of rivers
uint8 raw_industry_construction; ///< type of (raw) industry construction (none, "normal", prospecting)
uint8 industry_platform; ///< the amount of flat land around an industry
bool freeform_edges; ///< allow terraforming the tiles at the map edges
Expand Down
2 changes: 2 additions & 0 deletions src/station_cmd.cpp
Expand Up @@ -2549,6 +2549,7 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32

/* Get the water class of the water tile before it is cleared.*/
WaterClass wc = GetWaterClass(tile_cur);
bool river = HasTileCanalOnRiver(tile_cur);

ret = DoCommand(tile_cur, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
if (ret.Failed()) return ret;
Expand Down Expand Up @@ -2587,6 +2588,7 @@ CommandCost CmdBuildDock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
Company::Get(st->owner)->infrastructure.station += 2;

MakeDock(tile, st->owner, st->index, direction, wc);
if (river) SetCanalOnRiver(tile + TileOffsByDiagDir(direction));
UpdateStationDockingTiles(st);

st->AfterStationTileSetChange(true, STATION_DOCK);
Expand Down
2 changes: 1 addition & 1 deletion src/station_map.h
Expand Up @@ -542,7 +542,7 @@ static inline void MakeStation(TileIndex t, Owner o, StationID sid, StationType
SB(_me[t].m6, 2, 1, 0);
SB(_me[t].m6, 3, 3, st);
_me[t].m7 = 0;
_me[t].m8 = 0;
SB(_me[t].m8, 0, 15, 0);
}

/**
Expand Down
8 changes: 8 additions & 0 deletions src/table/settings/world_settings.ini
Expand Up @@ -429,6 +429,14 @@ def = true
str = STR_CONFIG_SETTING_EXTRADYNAMITE
strhelp = STR_CONFIG_SETTING_EXTRADYNAMITE_HELPTEXT

[SDT_BOOL]
var = construction.dynamite_river
from = SLV_PERMANENT_RIVERS
def = true
str = STR_CONFIG_SETTING_DYNAMITE_RIVER
strhelp = STR_CONFIG_SETTING_DYNAMITE_RIVER_HELPTEXT
cat = SC_BASIC

[SDT_VAR]
var = construction.max_bridge_length
type = SLE_UINT16
Expand Down
73 changes: 63 additions & 10 deletions src/water_cmd.cpp
Expand Up @@ -38,6 +38,7 @@
#include "company_gui.h"
#include "newgrf_generic.h"
#include "industry.h"
#include "cheat_type.h"

#include "table/strings.h"

Expand Down Expand Up @@ -240,15 +241,27 @@ void MakeWaterKeepingClass(TileIndex tile, Owner o)
wc = WATER_CLASS_CANAL;
}

bool river = HasTileCanalOnRiver(tile);
/* Zero map array and terminate animation */
DoClearSquare(tile);

/* Maybe change to water */
switch (wc) {
case WATER_CLASS_SEA: MakeSea(tile); break;
case WATER_CLASS_CANAL: MakeCanal(tile, o, Random()); break;
case WATER_CLASS_RIVER: MakeRiver(tile, Random()); break;
default: break;
case WATER_CLASS_SEA:
MakeSea(tile);
break;

case WATER_CLASS_CANAL:
MakeCanal(tile, o, Random());
if (river) SetCanalOnRiver(tile);
break;

case WATER_CLASS_RIVER:
MakeRiver(tile, Random());
break;

default:
break;
}

if (wc != WATER_CLASS_INVALID) CheckForDockingTile(tile);
Expand Down Expand Up @@ -479,6 +492,7 @@ CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
if (IsTileType(current_tile, MP_WATER) && (!IsTileOwner(current_tile, OWNER_WATER) || wc == WATER_CLASS_SEA)) continue;

bool water = IsWaterTile(current_tile);
bool river = HasTileWaterClass(current_tile) && GetWaterClass(current_tile) == WATER_CLASS_RIVER;
ret = DoCommand(current_tile, 0, 0, flags | DC_FORCE_CLEAR_TILE, CMD_LANDSCAPE_CLEAR);
if (ret.Failed()) return ret;

Expand All @@ -503,6 +517,7 @@ CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32

default:
MakeCanal(current_tile, _current_company, Random());
if (river) SetCanalOnRiver(current_tile);
if (Company::IsValidID(_current_company)) {
Company::Get(_current_company)->infrastructure.water++;
DirtyCompanyInfrastructureWindows(_current_company);
Expand Down Expand Up @@ -547,15 +562,24 @@ static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
if (ret.Failed()) return ret;
}

if (IsRiver(tile) && _game_mode == GM_NORMAL && !_settings_game.construction.dynamite_river && !_cheats.magic_bulldozer.value) {
return CommandCost();
}

if (flags & DC_EXEC) {
if (IsCanal(tile) && Company::IsValidID(owner)) {
Company::Get(owner)->infrastructure.water--;
DirtyCompanyInfrastructureWindows(owner);
}
bool remove = IsDockingTile(tile);
bool river = HasTileCanalOnRiver(tile);
DoClearSquare(tile);
if (river) {
MakeRiver(tile, Random());
} else if (remove) {
RemoveDockingTile(tile);
}
MarkCanalsAndRiversAroundDirty(tile);
if (remove) RemoveDockingTile(tile);
}

return CommandCost(EXPENSES_CONSTRUCTION, base_cost);
Expand Down Expand Up @@ -959,10 +983,24 @@ static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
switch (GetWaterTileType(tile)) {
case WATER_TILE_CLEAR:
switch (GetWaterClass(tile)) {
case WATER_CLASS_SEA: td->str = STR_LAI_WATER_DESCRIPTION_WATER; break;
case WATER_CLASS_CANAL: td->str = STR_LAI_WATER_DESCRIPTION_CANAL; break;
case WATER_CLASS_RIVER: td->str = STR_LAI_WATER_DESCRIPTION_RIVER; break;
default: NOT_REACHED();
case WATER_CLASS_SEA:
td->str = STR_LAI_WATER_DESCRIPTION_WATER;
break;

case WATER_CLASS_CANAL:
if (HasTileCanalOnRiver(tile)) {
td->str = STR_LAI_WATER_DESCRIPTION_CANALISED_RIVER;
} else {
td->str = STR_LAI_WATER_DESCRIPTION_CANAL;
}
break;

case WATER_CLASS_RIVER:
td->str = STR_LAI_WATER_DESCRIPTION_RIVER;
break;

default:
NOT_REACHED();
}
break;
case WATER_TILE_COAST: td->str = STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK; break;
Expand Down Expand Up @@ -1377,8 +1415,23 @@ static VehicleEnterTileStatus VehicleEnter_Water(Vehicle *v, TileIndex tile, int

static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new)
{
bool no_err_message = _game_mode == GM_NORMAL && !_settings_game.construction.dynamite_river && !_cheats.magic_bulldozer.value;

/* Canals can't be terraformed */
if (IsWaterTile(tile) && IsCanal(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST);
if (IsCanal(tile)) {
if (HasTileCanalOnRiver(tile) && no_err_message) {
return CMD_ERROR;
}
return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST);
}

/* Rivers can't be terraformed */
if (IsRiver(tile)) {
if (no_err_message) {
return CMD_ERROR;
}
return_cmd_error(STR_ERROR_MUST_CLEAR_RIVER_FIRST);
}

return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
}
Expand Down

0 comments on commit 150d825

Please sign in to comment.