/**
* Build a piece of canal.
* @param tile end tile of stretch-dragging
* @param flags type of operation
* @param p1 start tile of stretch-dragging
* @param p2 waterclass to build. sea and river can only be built in scenario editor
* @param text unused
* @return the cost of this operation or an error
*/
CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
WaterClass wc = Extract<WaterClass, 0, 2>(p2);
if (p1 >= MapSize() || wc == WATER_CLASS_INVALID) return CMD_ERROR;
/* Outside of the editor you can only build canals, not oceans */
if (wc != WATER_CLASS_CANAL && _game_mode != GM_EDITOR) return CMD_ERROR;
TileArea ta(tile, p1);
/* Outside the editor you can only drag canals, and not areas */
if (_game_mode != GM_EDITOR && ta.w != 1 && ta.h != 1) return CMD_ERROR;
Axis axis = INVALID_AXIS;
int delta = 0;
TileIndex tile_next = min(tile, p1);
int rem = 1; // Single-click mode.
/* Maybe change to Single-line mode. */
if (ta.w == 1 && ta.h > 1) {
rem = ta.h;
axis = AXIS_Y;
}
if (ta.w > 1 && ta.h == 1) {
rem = ta.w;
axis = AXIS_X;
}
/* Maybe change to Area-dragging mode. */
if (ta.w > 1 && ta.h > 1) rem = -1;
if (axis != INVALID_AXIS) delta = TileOffsByDiagDir(AxisToDiagDir(axis));
bool built_lock = false;
CommandCost cost(EXPENSES_CONSTRUCTION);
TILE_AREA_LOOP(tile, ta) {
if (rem > 0) rem--;
tile_next += delta;
if (built_lock) {
/* A lock was built on the last iteration, which means
* this iteration's tile belongs to the lock. */
built_lock = false;
continue;
}
Slope slope = GetTileSlope(tile);
if (slope != SLOPE_FLAT && !IsInclinedSlope(slope)) {
return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
}
/* From here on, slope is either flat or inclined. */
/* Can't make water of water! */
if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || wc == WATER_CLASS_SEA)) continue;
CommandCost ret;
bool river = HasTileWaterClass(tile) && GetWaterClass(tile) == WATER_CLASS_RIVER;
Slope slope_next = GetTileSlope(tile_next);
if (rem > 0 && wc == WATER_CLASS_CANAL && slope == SLOPE_FLAT && IsInclinedSlope(slope_next) && DiagDirToAxis(GetInclinedSlopeDirection(slope_next)) == axis) {
/* The conditions indicate a lock might be built on next iteration.
* Don't clear this tile and skip to the next iteration now. */
continue;
} else {
bool water = IsWaterTile(tile);
if (rem >= 0) { // Single-line or single-clicking.
if (wc == WATER_CLASS_CANAL && IsInclinedSlope(slope)) {
/* Try to build a lock on inclined tile. */
if ((axis == INVALID_AXIS || DiagDirToAxis(GetInclinedSlopeDirection(slope)) == axis)) {
int tile_delta = TileOffsByDiagDir(GetInclinedSlopeDirection(slope));
if (rem == 0 && axis == INVALID_AXIS || IsTileFlat(tile + tile_delta) && IsTileFlat(tile - tile_delta)) {
ret = DoCommand(tile, 0, 0, flags, CMD_BUILD_LOCK);
if (ret.Failed()) return ret;
cost.AddCost(ret);
built_lock = true;
} else {
return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
}
} else {
if (DiagDirToAxis(GetInclinedSlopeDirection(slope)) == OtherAxis(axis)) {
return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
}
}
} else {
ret = DoCommand(tile, 0, 0, flags | DC_FORCE_CLEAR_TILE, CMD_LANDSCAPE_CLEAR);
if (ret.Failed()) return ret;
if (!water) cost.AddCost(ret);
}
} else { // Area-dragging mode.
if (wc == WATER_CLASS_CANAL && IsInclinedSlope(slope)) {
return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
} else {
ret = DoCommand(tile, 0, 0, flags | DC_FORCE_CLEAR_TILE, CMD_LANDSCAPE_CLEAR);
if (ret.Failed()) return ret;
if (!water) cost.AddCost(ret);
}
}
}
if (flags & DC_EXEC) {
switch (wc) {
case WATER_CLASS_RIVER:
MakeRiver(tile, Random());
if (_game_mode == GM_EDITOR) {
TileIndex tile2 = tile;
CircularTileSearch(&tile2, 5, RiverModifyDesertZone, NULL);
}
break;
case WATER_CLASS_SEA:
if (TileHeight(tile) == 0) {
MakeSea(tile);
break;
}
/* FALL THROUGH */
default:
if (!built_lock) {
MakeCanal(tile, _current_company, Random());
if (river) SetCanalOnRiver(tile);
if (Company::IsValidID(_current_company)) {
Company::Get(_current_company)->infrastructure.water++;
DirtyCompanyInfrastructureWindows(_current_company);
}
}
break;
}
MarkTileDirtyByTile(tile);
MarkCanalsAndRiversAroundDirty(tile);
}
if (!built_lock) cost.AddCost(_price[PR_BUILD_CANAL]);
}
if (cost.GetCost() == 0) {
return_cmd_error(STR_ERROR_ALREADY_BUILT);
} else {
return cost;
}
}