diff --git a/road_cmd.c b/road_cmd.c index 116efd5476..6c06809a23 100644 --- a/road_cmd.c +++ b/road_cmd.c @@ -293,7 +293,7 @@ int32 CmdBuildRoad(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) /* Road pieces are max 4 bitset values (NE, NW, SE, SW) and town can only be non-zero * if a non-player is building the road */ - if ((p1 >> 4) || (IsValidPlayer(_current_player) && p2 != 0) || !IsValidTownID(p2)) return CMD_ERROR; + if ((p1 >> 4) || (IsValidPlayer(_current_player) && p2 != 0) || (_current_player == OWNER_TOWN && !IsValidTownID(p2))) return CMD_ERROR; pieces = p1; tileh = GetTileSlope(tile, NULL); diff --git a/station_cmd.c b/station_cmd.c index 625d916af3..7929c23424 100644 --- a/station_cmd.c +++ b/station_cmd.c @@ -1895,13 +1895,22 @@ static int32 RemoveBuoy(Station *st, uint32 flags) if (!EnsureNoVehicle(tile)) return CMD_ERROR; if (flags & DC_EXEC) { + Owner o; st->dock_tile = 0; /* Buoys are marked in the Station struct by this flag. Yes, it is this * braindead.. */ st->facilities &= ~FACIL_DOCK; st->had_vehicle_of_type &= ~HVOT_BUOY; - MakeWater(tile); + /* We have to set the water tile's state to the same state as before the + * buoy was placed. Otherwise one could plant a buoy on a canal edge, + * remove it and flood the land (if the canal edge is at level 0) */ + o = GetTileOwner(tile); + if (o == OWNER_WATER) { + MakeWater(tile); + } else { + MakeCanal(tile, o); + } MarkTileDirtyByTile(tile); UpdateStationVirtCoordDirty(st); diff --git a/station_map.h b/station_map.h index c52769b141..0adbd92b30 100644 --- a/station_map.h +++ b/station_map.h @@ -287,7 +287,10 @@ static inline void MakeAirport(TileIndex t, Owner o, StationID sid, byte section static inline void MakeBuoy(TileIndex t, StationID sid) { - MakeStation(t, OWNER_NONE, sid, GFX_BUOY_BASE); + /* Make the owner of the buoy tile the same as the current owner of the + * water tile. In this way, we can reset the owner of the water to its + * original state when the buoy gets removed. */ + MakeStation(t, GetTileOwner(t), sid, GFX_BUOY_BASE); } static inline void MakeDock(TileIndex t, Owner o, StationID sid, DiagDirection d) diff --git a/tunnelbridge_cmd.c b/tunnelbridge_cmd.c index 7cc2a8ce77..1f24daf264 100644 --- a/tunnelbridge_cmd.c +++ b/tunnelbridge_cmd.c @@ -541,7 +541,7 @@ TileIndex CheckTunnelBusy(TileIndex tile, uint *length) GetTileZ(tile) != z ); - v = FindVehicleBetween(starttile, tile, z); + v = FindVehicleBetween(starttile, tile, z, false); if (v != NULL) { _error_message = v->type == VEH_Train ? STR_5000_TRAIN_IN_TUNNEL : STR_5001_ROAD_VEHICLE_IN_TUNNEL; @@ -688,7 +688,7 @@ static int32 DoClearBridge(TileIndex tile, uint32 flags) v = FindVehicleBetween( tile + delta, endtile - delta, - GetBridgeHeightRamp(tile) + GetBridgeHeightRamp(tile), false ); if (v != NULL) return_cmd_error(VehicleInTheWayErrMsg(v)); @@ -820,7 +820,7 @@ int32 DoConvertTunnelBridgeRail(TileIndex tile, RailType totype, bool exec) if (!EnsureNoVehicle(tile) || !EnsureNoVehicle(endtile) || - FindVehicleBetween(tile, endtile, GetBridgeHeightRamp(tile)) != NULL) { + FindVehicleBetween(tile, endtile, GetBridgeHeightRamp(tile), false) != NULL) { return_cmd_error(STR_8803_TRAIN_IN_THE_WAY); } diff --git a/vehicle.c b/vehicle.c index e5ee778ecb..7b1dde8e7c 100644 --- a/vehicle.c +++ b/vehicle.c @@ -174,7 +174,7 @@ Vehicle *FindVehicleOnTileZ(TileIndex tile, byte z) return VehicleFromPos(tile, &ti, EnsureNoVehicleProcZ); } -Vehicle *FindVehicleBetween(TileIndex from, TileIndex to, byte z) +Vehicle *FindVehicleBetween(TileIndex from, TileIndex to, byte z, bool without_crashed) { int x1 = TileX(from); int y1 = TileY(from); @@ -188,6 +188,7 @@ Vehicle *FindVehicleBetween(TileIndex from, TileIndex to, byte z) intswap(y1,y2); } FOR_ALL_VEHICLES(veh) { + if (without_crashed && (veh->vehstatus & VS_CRASHED) != 0) continue; if ((veh->type == VEH_Train || veh->type == VEH_Road) && (z==0xFF || veh->z_pos == z)) { if ((veh->x_pos>>4) >= x1 && (veh->x_pos>>4) <= x2 && (veh->y_pos>>4) >= y1 && (veh->y_pos>>4) <= y2) { diff --git a/vehicle.h b/vehicle.h index 20c1b70184..a4377572de 100644 --- a/vehicle.h +++ b/vehicle.h @@ -299,7 +299,7 @@ Vehicle *CreateEffectVehicleRel(const Vehicle *v, int x, int y, int z, EffectVeh uint32 VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y); StringID VehicleInTheWayErrMsg(const Vehicle* v); -Vehicle *FindVehicleBetween(TileIndex from, TileIndex to, byte z); +Vehicle *FindVehicleBetween(TileIndex from, TileIndex to, byte z, bool without_crashed); TileIndex GetVehicleOutOfTunnelTile(const Vehicle *v); bool UpdateSignalsOnSegment(TileIndex tile, DiagDirection direction); diff --git a/water_cmd.c b/water_cmd.c index 8af293b95c..81ca489295 100644 --- a/water_cmd.c +++ b/water_cmd.c @@ -40,6 +40,7 @@ const SpriteID _water_shore_sprites[15] = { }; +static Vehicle *FindFloodableVehicleOnTile(TileIndex tile); static void FloodVehicle(Vehicle *v); /** Build a ship depot. @@ -593,7 +594,7 @@ static void TileLoopWaterHelper(TileIndex tile, const TileIndexDiffC *offs) _current_player = OWNER_WATER; { - Vehicle *v = FindVehicleOnTileZ(target, 0); + Vehicle *v = FindFloodableVehicleOnTile(target); if (v != NULL) FloodVehicle(v); } @@ -604,6 +605,36 @@ static void TileLoopWaterHelper(TileIndex tile, const TileIndexDiffC *offs) } } +/** + * Finds a vehicle to flood. + * It does not find vehicles that are already crashed on bridges, i.e. flooded. + * @param tile the tile where to find a vehicle to flood + * @return a vehicle too flood or NULL when there is no vehicle too flood. + */ +static Vehicle *FindFloodableVehicleOnTile(TileIndex tile) +{ + TileIndex end; + byte z; + Vehicle *v; + + if (!IsBridgeTile(tile)) return FindVehicleOnTileZ(tile, 0); + + end = GetOtherBridgeEnd(tile); + z = GetBridgeHeight(tile); + + /* check the start tile first since as this is closest to the water */ + v = FindVehicleOnTileZ(tile, z); + if (v != NULL && (v->vehstatus & VS_CRASHED) == 0) return v; + + /* check a vehicle in between both bridge heads */ + v = FindVehicleBetween(tile, end, z, true); + if (v != NULL) return v; + + /* check the end tile last to give fleeing vehicles a chance to escape */ + v = FindVehicleOnTileZ(end, z); + return (v != NULL && (v->vehstatus & VS_CRASHED) == 0) ? v : NULL; +} + static void FloodVehicle(Vehicle *v) { if (!(v->vehstatus & VS_CRASHED)) { @@ -628,6 +659,7 @@ static void FloodVehicle(Vehicle *v) BEGIN_ENUM_WAGONS(v) if (v->cargo_type == CT_PASSENGERS) pass += v->cargo_count; v->vehstatus |= VS_CRASHED; + MarkAllViewportsDirty(v->left_coord, v->top_coord, v->right_coord + 1, v->bottom_coord + 1); END_ENUM_WAGONS(v) v = u; @@ -661,9 +693,8 @@ void TileLoop_Water(TileIndex tile) {{ 0, -1}, {0, 0}, {1, 0}, { 0, -1}, { 1, -1}} }; - /* Ensure sea-level canals do not flood */ - if ((IsTileType(tile, MP_WATER) || IsTileType(tile, MP_TUNNELBRIDGE)) && - !IsTileOwner(tile, OWNER_WATER)) return; + /* Ensure sea-level canals and buoys on canal borders do not flood */ + if ((IsTileType(tile, MP_WATER) || IsTileType(tile, MP_TUNNELBRIDGE) || IsBuoyTile(tile)) && !IsTileOwner(tile, OWNER_WATER)) return; if (IS_INT_INSIDE(TileX(tile), 1, MapSizeX() - 3 + 1) && IS_INT_INSIDE(TileY(tile), 1, MapSizeY() - 3 + 1)) {