/** * 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(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; } }