/** * Builds a lock. * @param tile Central tile of the lock. * @param dir Uphill direction. * @param flags Operation to perform. * @return The cost in case of success, or an error code if it failed. */ static CommandCost DoBuildLock(TileIndex tile, DiagDirection dir, DoCommandFlag flags) { CommandCost cost(EXPENSES_CONSTRUCTION); int delta = TileOffsByDiagDir(dir); CommandCost ret = EnsureNoVehicleOnGround(tile); if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta); if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta); if (ret.Failed()) return ret; /* middle tile */ ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; cost.AddCost(ret); /* lower tile */ WaterClass wc_lower = IsWaterTile(tile - delta) ? GetWaterClass(tile - delta) : WATER_CLASS_CANAL; if (!IsWaterTile(tile - delta)) { ret = DoCommand(tile - delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; cost.AddCost(ret); cost.AddCost(_price[PR_BUILD_CANAL]); } if (GetTileSlope(tile - delta, NULL) != SLOPE_FLAT) { return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); } /* upper tile */ WaterClass wc_upper = IsWaterTile(tile + delta) ? GetWaterClass(tile + delta) : WATER_CLASS_CANAL; if (!IsWaterTile(tile + delta)) { ret = DoCommand(tile + delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; cost.AddCost(ret); cost.AddCost(_price[PR_BUILD_CANAL]); } if (GetTileSlope(tile + delta, NULL) != SLOPE_FLAT) { return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); } if ((MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) || (MayHaveBridgeAbove(tile - delta) && IsBridgeAbove(tile - delta)) || (MayHaveBridgeAbove(tile + delta) && IsBridgeAbove(tile + delta))) { return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); } if (flags & DC_EXEC) { MakeLock(tile, _current_company, dir, wc_lower, wc_upper); MarkTileDirtyByTile(tile); MarkTileDirtyByTile(tile - delta); MarkTileDirtyByTile(tile + delta); MarkCanalsAndRiversAroundDirty(tile - delta); MarkCanalsAndRiversAroundDirty(tile + delta); } cost.AddCost(_price[PR_BUILD_LOCK]); return cost; }
/** * Build a ship depot. * @param tile tile where ship depot is built * @param flags type of operation * @param p1 bit 0 depot orientation (Axis) * @param p2 unused * @param text unused * @return the cost of this operation or an error */ CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { Axis axis = Extract<Axis, 0, 1>(p1); TileIndex tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) { return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER); } if ((MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) || (MayHaveBridgeAbove(tile2) && IsBridgeAbove(tile2))) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); if (GetTileSlope(tile, NULL) != SLOPE_FLAT || GetTileSlope(tile2, NULL) != SLOPE_FLAT) { /* Prevent depots on rapids */ return_cmd_error(STR_ERROR_SITE_UNSUITABLE); } if (!Depot::CanAllocateItem()) return CMD_ERROR; WaterClass wc1 = GetWaterClass(tile); WaterClass wc2 = GetWaterClass(tile2); CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]); bool add_cost = !IsWaterTile(tile); CommandCost ret = DoCommand(tile, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; if (add_cost) { cost.AddCost(ret); } add_cost = !IsWaterTile(tile2); ret = DoCommand(tile2, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; if (add_cost) { cost.AddCost(ret); } if (flags & DC_EXEC) { Depot *depot = new Depot(tile); depot->build_date = _date; MakeShipDepot(tile, _current_company, depot->index, DEPOT_NORTH, axis, wc1); MakeShipDepot(tile2, _current_company, depot->index, DEPOT_SOUTH, axis, wc2); MarkTileDirtyByTile(tile); MarkTileDirtyByTile(tile2); MakeDefaultName(depot); } return cost; }
static uint GetSlopeZ_Clear(TileIndex tile, uint x, uint y) { uint z; Slope tileh = GetTileSlope(tile, &z); return z + GetPartialZ(x & 0xF, y & 0xF, tileh); }
/** * Check whether the given tile is suitable for a waypoint. * @param tile the tile to check for suitability * @param axis the axis of the waypoint * @param waypoint Waypoint the waypoint to check for is already joined to. If we find another waypoint it can join to it will throw an error. */ static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *waypoint) { /* if waypoint is set, then we have special handling to allow building on top of already existing waypoints. * so waypoint points to INVALID_STATION if we can build on any waypoint. * Or it points to a waypoint if we're only allowed to build on exactly that waypoint. */ if (waypoint != NULL && IsTileType(tile, MP_STATION)) { if (!IsRailWaypoint(tile)) { return ClearTile_Station(tile, DC_AUTO); // get error message } else { StationID wp = GetStationIndex(tile); if (*waypoint == INVALID_STATION) { *waypoint = wp; } else if (*waypoint != wp) { return_cmd_error(STR_ERROR_WAYPOINT_ADJOINS_MORE_THAN_ONE_EXISTING); } } } if (GetAxisForNewWaypoint(tile) != axis) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); Owner owner = GetTileOwner(tile); CommandCost ret = CheckOwnership(owner); if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile); if (ret.Failed()) return ret; Slope tileh = GetTileSlope(tile); if (tileh != SLOPE_FLAT && (!_settings_game.construction.build_on_slopes || IsSteepSlope(tileh) || !(tileh & (0x3 << axis)) || !(tileh & ~(0x3 << axis)))) { return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); } if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); return CommandCost(); }
/** * Builds a lock. * @param tile tile where to place the lock * @param flags type of operation * @param p1 unused * @param p2 unused * @param text unused * @return the cost of this operation or an error */ CommandCost CmdBuildLock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile)); if (dir == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); return DoBuildLock(tile, dir, flags); }
virtual void OnPlacePresize(Point pt, TileIndex tile_from) { DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile_from, NULL)); TileIndex tile_to = (dir != INVALID_DIAGDIR ? TileAddByDiagDir(tile_from, ReverseDiagDir(dir)) : tile_from); VpSetPresizeRange(tile_from, tile_to); }
/** * Gets the other end of the aqueduct, if possible. * @param tile_from The begin tile for the aqueduct. * @param [out] tile_to The tile till where to show a selection for the aqueduct. * @return The other end of the aqueduct, or otherwise a tile in line with the aqueduct to cause the right error message. */ static TileIndex GetOtherAqueductEnd(TileIndex tile_from, TileIndex *tile_to = NULL) { int z; DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile_from, &z)); /* If the direction isn't right, just return the next tile so the command * complains about the wrong slope instead of the ends not matching up. * Make sure the coordinate is always a valid tile within the map, so we * don't go "off" the map. That would cause the wrong error message. */ if (!IsValidDiagDirection(dir)) return TILE_ADDXY(tile_from, TileX(tile_from) > 2 ? -1 : 1, 0); /* Direction the aqueduct is built to. */ TileIndexDiff offset = TileOffsByDiagDir(ReverseDiagDir(dir)); /* The maximum length of the aqueduct. */ int max_length = min(_settings_game.construction.max_bridge_length, DistanceFromEdgeDir(tile_from, ReverseDiagDir(dir)) - 1); TileIndex endtile = tile_from; for (int length = 0; IsValidTile(endtile) && TileX(endtile) != 0 && TileY(endtile) != 0; length++) { endtile = TILE_ADD(endtile, offset); if (length > max_length) break; if (GetTileMaxZ(endtile) > z) { if (tile_to != NULL) *tile_to = endtile; break; } } return endtile; }
/** build a shiplift */ static CommandCost DoBuildShiplift(TileIndex tile, DiagDirection dir, DoCommandFlag flags) { CommandCost ret; int delta; /* middle tile */ ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (CmdFailed(ret)) return CMD_ERROR; delta = TileOffsByDiagDir(dir); /* lower tile */ WaterClass wc_lower = IsWaterTile(tile - delta) ? GetWaterClass(tile - delta) : WATER_CLASS_CANAL; ret = DoCommand(tile - delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (CmdFailed(ret)) return CMD_ERROR; if (GetTileSlope(tile - delta, NULL) != SLOPE_FLAT) { return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); } /* upper tile */ WaterClass wc_upper = IsWaterTile(tile + delta) ? GetWaterClass(tile + delta) : WATER_CLASS_CANAL; ret = DoCommand(tile + delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (CmdFailed(ret)) return CMD_ERROR; if (GetTileSlope(tile + delta, NULL) != SLOPE_FLAT) { return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); } if ((MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) || (MayHaveBridgeAbove(tile - delta) && IsBridgeAbove(tile - delta)) || (MayHaveBridgeAbove(tile + delta) && IsBridgeAbove(tile + delta))) { return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); } if (flags & DC_EXEC) { MakeLock(tile, _current_company, dir, wc_lower, wc_upper); MarkTileDirtyByTile(tile); MarkTileDirtyByTile(tile - delta); MarkTileDirtyByTile(tile + delta); MarkCanalsAndRiversAroundDirty(tile - delta); MarkCanalsAndRiversAroundDirty(tile + delta); } return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER] * 22 >> 3); }
/** * Get the height ('z') of a bridge in pixels. * @param tile the bridge ramp tile to get the bridge height from * @return the height of the bridge in pixels */ uint GetBridgeHeight(TileIndex t) { uint h; Slope tileh = GetTileSlope(t, &h); Foundation f = GetBridgeFoundation(tileh, DiagDirToAxis(GetTunnelBridgeDirection(t))); /* one height level extra for the ramp */ return h + TILE_HEIGHT + ApplyFoundationToSlope(f, &tileh); }
int GetBridgeHeight(typename TileIndexT<Tgeneric>::T t) { int h; Slope tileh = GetTileSlope(t, &h); Foundation f = GetBridgeFoundation(tileh, DiagDirToAxis(GetTunnelBridgeDirection(t))); /* one height level extra for the ramp */ return h + 1 + ApplyFoundationToSlope(f, &tileh); }
/** * Builds a lock. * @param tile tile where to place the lock * @param flags type of operation * @param p1 unused * @param p2 unused * @param text unused * @return the cost of this operation or an error */ CommandCost CmdBuildLock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile, NULL)); if (dir == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); /* Disallow building of locks on river rapids */ if (IsWaterTile(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); return DoBuildLock(tile, dir, flags); }
/** * Does the given track direction on the given tile yeild an uphill penalty? * @param tile The tile to check. * @param td The track direction to check. * @return True if there's a slope, otherwise false. */ FORCEINLINE static bool stSlopeCost(TileIndex tile, Trackdir td) { if (IsDiagonalTrackdir(td)) { if (IsBridgeTile(tile)) { /* it is bridge ramp, check if we are entering the bridge */ if (GetTunnelBridgeDirection(tile) != TrackdirToExitdir(td)) return false; // no, we are leaving it, no penalty /* we are entering the bridge */ Slope tile_slope = GetTileSlope(tile, NULL); Axis axis = DiagDirToAxis(GetTunnelBridgeDirection(tile)); return !HasBridgeFlatRamp(tile_slope, axis); } else { /* not bridge ramp */ if (IsTunnelTile(tile)) return false; // tunnel entry/exit doesn't slope Slope tile_slope = GetTileSlope(tile, NULL); return IsUphillTrackdir(tile_slope, td); // slopes uphill => apply penalty } } return false; }
/** Build a ship depot. * @param tile tile where ship depot is built * @param flags type of operation * @param p1 bit 0 depot orientation (Axis) * @param p2 unused * @param text unused * @return the cost of this operation or an error */ CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { TileIndex tile2; CommandCost ret; Axis axis = Extract<Axis, 0>(p1); tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); if (!IsWaterTile(tile) || !IsWaterTile(tile2)) { return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER); } if (IsBridgeAbove(tile) || IsBridgeAbove(tile2)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); if (GetTileSlope(tile, NULL) != SLOPE_FLAT || GetTileSlope(tile2, NULL) != SLOPE_FLAT) { /* Prevent depots on rapids */ return_cmd_error(STR_ERROR_SITE_UNSUITABLE); } WaterClass wc1 = GetWaterClass(tile); WaterClass wc2 = GetWaterClass(tile2); ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (CmdFailed(ret)) return CMD_ERROR; ret = DoCommand(tile2, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (CmdFailed(ret)) return CMD_ERROR; if (!Depot::CanAllocateItem()) return CMD_ERROR; if (flags & DC_EXEC) { Depot *depot = new Depot(tile); depot->town_index = ClosestTownFromTile(tile, UINT_MAX)->index; MakeShipDepot(tile, _current_company, depot->index, DEPOT_NORTH, axis, wc1); MakeShipDepot(tile2, _current_company, depot->index, DEPOT_SOUTH, axis, wc2); MarkTileDirtyByTile(tile); MarkTileDirtyByTile(tile2); } return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]); }
/** * 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; CommandCost cost(EXPENSES_CONSTRUCTION); TILE_AREA_LOOP(tile, ta) { CommandCost ret; Slope slope = GetTileSlope(tile, NULL); if (slope != SLOPE_FLAT && (wc != WATER_CLASS_RIVER || !IsInclinedSlope(slope))) { return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); } /* can't make water of water! */ if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || wc == WATER_CLASS_SEA)) continue; ret = DoCommand(tile, 0, 0, flags | DC_FORCE_CLEAR_TILE, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; cost.AddCost(ret); if (flags & DC_EXEC) { switch (wc) { case WATER_CLASS_RIVER: MakeRiver(tile, Random()); break; case WATER_CLASS_SEA: if (TileHeight(tile) == 0) { MakeSea(tile); break; } /* FALL THROUGH */ default: MakeCanal(tile, _current_company, Random()); break; } MarkTileDirtyByTile(tile); MarkCanalsAndRiversAroundDirty(tile); } cost.AddCost(_price[PR_BUILD_CANAL]); }
/** * Tests if a tile can be converted to MP_TREES * This is true for clear ground without farms or rocks. * * @param tile the tile of interest * @param allow_desert Allow planting trees on CLEAR_DESERT? * @return true if trees can be built. */ static bool CanPlantTreesOnTile(TileIndex tile, bool allow_desert) { switch (GetTileType(tile)) { case MP_WATER: return !IsBridgeAbove(tile) && IsCoast(tile) && !IsSlopeWithOneCornerRaised(GetTileSlope(tile, NULL)); case MP_CLEAR: return !IsBridgeAbove(tile) && !IsClearGround(tile, CLEAR_FIELDS) && GetRawClearGround(tile) != CLEAR_ROCKS && (allow_desert || !IsClearGround(tile, CLEAR_DESERT)); default: return false; } }
void MakeWaterKeepingClass(TileIndex tile, Owner o) { WaterClass wc = GetWaterClass(tile); /* Autoslope might turn an originally canal or river tile into land */ int z; Slope slope = GetTileSlope(tile, &z); if (slope != SLOPE_FLAT) { if (wc == WATER_CLASS_CANAL) { /* If we clear the canal, we have to remove it from the infrastructure count as well. */ Company *c = Company::GetIfValid(o); if (c != NULL) { c->infrastructure.water--; DirtyCompanyInfrastructureWindows(c->index); } /* Sloped canals are locks and no natural water remains whatever the slope direction */ wc = WATER_CLASS_INVALID; } /* Only river water should be restored on appropriate slopes. Other water would be invalid on slopes */ if (wc != WATER_CLASS_RIVER || GetInclinedSlopeDirection(slope) == INVALID_DIAGDIR) { wc = WATER_CLASS_INVALID; } } if (wc == WATER_CLASS_SEA && z > 0) { /* Update company infrastructure count. */ Company *c = Company::GetIfValid(o); if (c != NULL) { c->infrastructure.water++; DirtyCompanyInfrastructureWindows(c->index); } wc = WATER_CLASS_CANAL; } /* 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; } MarkTileDirtyByTile(tile); }
virtual void OnPlaceObject(Point pt, TileIndex tile) { switch (this->last_clicked_widget) { case WID_DT_CANAL: // Build canal button VpStartPlaceSizing(tile, (_game_mode == GM_EDITOR) ? VPM_X_AND_Y : VPM_X_OR_Y, DDSP_CREATE_WATER); break; case WID_DT_LOCK: // Build lock button DoCommandP(tile, 0, 0, CMD_BUILD_LOCK | CMD_MSG(STR_ERROR_CAN_T_BUILD_LOCKS), CcBuildDocks); break; case WID_DT_DEMOLISH: // Demolish aka dynamite button PlaceProc_DemolishArea(tile); break; case WID_DT_DEPOT: // Build depot button DoCommandP(tile, _ship_depot_direction, 0, CMD_BUILD_SHIP_DEPOT | CMD_MSG(STR_ERROR_CAN_T_BUILD_SHIP_DEPOT), CcBuildDocks); break; case WID_DT_STATION: { // Build station button uint32 p2 = (uint32)INVALID_STATION << 16; // no station to join /* tile is always the land tile, so need to evaluate _thd.pos */ CommandContainer cmdcont = { tile, _ctrl_pressed, p2, CMD_BUILD_DOCK | CMD_MSG(STR_ERROR_CAN_T_BUILD_DOCK_HERE), CcBuildDocks, "" }; /* Determine the watery part of the dock. */ DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile)); TileIndex tile_to = (dir != INVALID_DIAGDIR ? TileAddByDiagDir(tile, ReverseDiagDir(dir)) : tile); ShowSelectStationIfNeeded(cmdcont, TileArea(tile, tile_to)); break; } case WID_DT_BUOY: // Build buoy button DoCommandP(tile, 0, 0, CMD_BUILD_BUOY | CMD_MSG(STR_ERROR_CAN_T_POSITION_BUOY_HERE), CcBuildDocks); break; case WID_DT_RIVER: // Build river button (in scenario editor) VpStartPlaceSizing(tile, VPM_X_AND_Y, DDSP_CREATE_RIVER); break; case WID_DT_BUILD_AQUEDUCT: // Build aqueduct button DoCommandP(tile, GetOtherAqueductEnd(tile), TRANSPORT_WATER << 15, CMD_BUILD_BRIDGE | CMD_MSG(STR_ERROR_CAN_T_BUILD_AQUEDUCT_HERE), CcBuildBridge); break; default: NOT_REACHED(); } }
/** * Check whether the given tile is suitable for a waypoint. * @param tile the tile to check for suitability * @param axis the axis of the waypoint * @param waypoint Waypoint the waypoint to check for is already joined to. If we find another waypoint it can join to it will throw an error. * @param flags flags for the command */ static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *waypoint, DoCommandFlag flags) { /* if waypoint is set, then we have special handling to allow building on top of already existing waypoints. * so waypoint points to INVALID_STATION if we can build on any waypoint. * Or it points to a waypoint if we're only allowed to build on exactly that waypoint. */ if (waypoint != NULL && IsTileType(tile, MP_STATION)) { if (!IsRailWaypoint(tile)) { return ClearTile_Station(tile, DC_AUTO); // get error message } else { StationID wp = GetStationIndex(tile); if (*waypoint == INVALID_STATION) { *waypoint = wp; } else if (*waypoint != wp) { return_cmd_error(STR_ERROR_WAYPOINT_ADJOINS_MORE_THAN_ONE_EXISTING); } } } /* When pasting a waypoint, there may be no track yet (it will be placed when DC_EXEC'ing). * Ignore that so we can calculate the cost more precisely. */ bool ignore_lack_of_tracks = (flags & DC_PASTE) && !(flags & DC_EXEC); if (!ignore_lack_of_tracks || IsTileType(tile, MP_RAILWAY)) { if (GetAxisForNewWaypoint(tile) != axis) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); } Owner owner = GetTileOwner(tile); if (!ignore_lack_of_tracks || owner != OWNER_NONE) { CommandCost ret = CheckOwnership(owner); if (ret.Failed()) return ret; } CommandCost ret = EnsureNoVehicleOnGround(tile); if (ret.Failed()) return ret; Slope tileh = GetTileSlope(tile); if (tileh != SLOPE_FLAT && (!_settings_game.construction.build_on_slopes || IsSteepSlope(tileh) || !(tileh & (0x3 << axis)) || !(tileh & ~(0x3 << axis)))) { return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); } if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); return CommandCost(); }
/** * Build a buoy. * @param tile tile where to place the buoy * @param flags operation to perform * @param p1 unused * @param p2 unused * @param text unused * @return the cost of this operation or an error */ CommandCost CmdBuildBuoy(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { if (tile == 0 || !HasTileWaterGround(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); if (GetTileSlope(tile) != SLOPE_FLAT) return_cmd_error(STR_ERROR_SITE_UNSUITABLE); /* Check if there is an already existing, deleted, waypoint close to us that we can reuse. */ Waypoint *wp = FindDeletedWaypointCloseTo(tile, STR_SV_STNAME_BUOY, OWNER_NONE); if (wp == NULL && !Waypoint::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING); CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_WAYPOINT_BUOY]); if (!IsWaterTile(tile)) { CommandCost ret = DoCommand(tile, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; cost.AddCost(ret); } if (flags & DC_EXEC) { if (wp == NULL) { wp = new Waypoint(tile); } else { /* Move existing (recently deleted) buoy to the new location */ wp->xy = tile; InvalidateWindowData(WC_WAYPOINT_VIEW, wp->index); } wp->rect.BeforeAddTile(tile, StationRect::ADD_TRY); wp->string_id = STR_SV_STNAME_BUOY; wp->facilities |= FACIL_DOCK; wp->owner = OWNER_NONE; wp->build_date = _date; if (wp->town == NULL) MakeDefaultName(wp); MakeBuoy(tile, wp->index, GetWaterClass(tile)); wp->UpdateVirtCoord(); InvalidateWindowData(WC_WAYPOINT_VIEW, wp->index); } return cost; }
virtual void OnPlacePresize(Point pt, TileIndex tile_from) { TileIndex tile_to = tile_from; if (this->last_clicked_widget == WID_DT_BUILD_AQUEDUCT) { GetOtherAqueductEnd(tile_from, &tile_to); } else { DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile_from)); if (IsValidDiagDirection(dir)) { /* Locks and docks always select the tile "down" the slope. */ tile_to = TileAddByDiagDir(tile_from, ReverseDiagDir(dir)); /* Locks also select the tile "up" the slope. */ if (this->last_clicked_widget == WID_DT_LOCK) tile_from = TileAddByDiagDir(tile_from, dir); } } VpSetPresizeRange(tile_from, tile_to); }
/** 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 specifies canal (0), water (1) or river (2); last two 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) { CommandCost cost(EXPENSES_CONSTRUCTION); if (p1 >= MapSize()) return CMD_ERROR; /* Outside of the editor you can only build canals, not oceans */ if (p2 != 0 && _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; TILE_AREA_LOOP(tile, ta) { CommandCost ret; Slope slope = GetTileSlope(tile, NULL); if (slope != SLOPE_FLAT && (p2 != 2 || !IsInclinedSlope(slope))) { return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); } /* can't make water of water! */ if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || p2 == 1)) continue; ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (CmdFailed(ret)) return ret; cost.AddCost(ret); if (flags & DC_EXEC) { if (TileHeight(tile) == 0 && p2 == 1) { MakeSea(tile); } else if (p2 == 2) { MakeRiver(tile, Random()); } else { MakeCanal(tile, _current_company, Random()); } MarkTileDirtyByTile(tile); MarkCanalsAndRiversAroundDirty(tile); } cost.AddCost(_price[PR_CLEAR_WATER]); }
/** * Check the slope of a tile of a new station. * @param north_tile Norther tile of the station rect. * @param cur_tile Tile to check. * @param statspec Station spec. * @param axis Axis of the new station. * @param plat_len Platform length. * @param numtracks Number of platforms. * @return Succeeded or failed command. */ CommandCost PerformStationTileSlopeCheck(TileIndex north_tile, TileIndex cur_tile, const StationSpec *statspec, Axis axis, byte plat_len, byte numtracks) { TileIndexDiff diff = cur_tile - north_tile; Slope slope = GetTileSlope(cur_tile); StationResolverObject object(statspec, NULL, cur_tile, CBID_STATION_LAND_SLOPE_CHECK, (slope << 4) | (slope ^ (axis == AXIS_Y && HasBit(slope, CORNER_W) != HasBit(slope, CORNER_E) ? SLOPE_EW : 0)), (numtracks << 24) | (plat_len << 16) | (axis == AXIS_Y ? TileX(diff) << 8 | TileY(diff) : TileY(diff) << 8 | TileX(diff))); object.station_scope.axis = axis; uint16 cb_res = object.ResolveCallback(); /* Failed callback means success. */ if (cb_res == CALLBACK_FAILED) return CommandCost(); /* The meaning of bit 10 is inverted for a grf version < 8. */ if (statspec->grf_prop.grffile->grf_version < 8) ToggleBit(cb_res, 10); return GetErrorMessageFromLocationCallbackResult(cb_res, statspec->grf_prop.grffile, STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); }
void MakeWaterKeepingClass(TileIndex tile, Owner o) { assert(IsTileType(tile, MP_WATER) || (IsTileType(tile, MP_STATION) && (IsBuoy(tile) || IsDock(tile) || IsOilRig(tile))) || IsTileType(tile, MP_INDUSTRY)); WaterClass wc = GetWaterClass(tile); /* Autoslope might turn an originally canal or river tile into land */ uint z; if (GetTileSlope(tile, &z) != SLOPE_FLAT) wc = WATER_CLASS_INVALID; if (wc == WATER_CLASS_SEA && z > 0) wc = WATER_CLASS_CANAL; 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: DoClearSquare(tile); break; } }
void MakeWaterKeepingClass(TileIndex tile, Owner o) { WaterClass wc = GetWaterClass(tile); /* Autoslope might turn an originally canal or river tile into land */ uint z; if (GetTileSlope(tile, &z) != SLOPE_FLAT) wc = WATER_CLASS_INVALID; if (wc == WATER_CLASS_SEA && z > 0) wc = WATER_CLASS_CANAL; /* 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; } MarkTileDirtyByTile(tile); }
/** * Build a ship depot. * @param tile tile where ship depot is built * @param flags type of operation * @param p1 bit 0 depot orientation (Axis) * @param p2 unused * @param text unused * @return the cost of this operation or an error */ CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { Axis axis = Extract<Axis, 0, 1>(p1); TileIndex tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); WaterClass wc1, wc2; if ((flags & DC_PASTE) && !(flags & DC_EXEC)) { /* When pasting a ship depot, there may be no water yet (a canal will be placed when DC_EXE'ing). * Ignore that there is no water so we can calculate the cost more precisely. */ wc1 = wc2 = WATER_CLASS_INVALID; } else { if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER); wc1 = GetWaterClass(tile); wc2 = GetWaterClass(tile2); } if ((MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) || (MayHaveBridgeAbove(tile2) && IsBridgeAbove(tile2))) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); if (GetTileSlope(tile) != SLOPE_FLAT || GetTileSlope(tile2) != SLOPE_FLAT) { /* Prevent depots on rapids */ return_cmd_error(STR_ERROR_SITE_UNSUITABLE); } if (!Depot::CanAllocateItem()) return CMD_ERROR; CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]); bool add_cost = !IsWaterTile(tile); CommandCost ret = DoCommand(tile, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; if (add_cost) { cost.AddCost(ret); } add_cost = !IsWaterTile(tile2); ret = DoCommand(tile2, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; if (add_cost) { cost.AddCost(ret); } if (flags & DC_EXEC) { Depot *depot = new Depot(tile); depot->build_date = _date; if (wc1 == WATER_CLASS_CANAL || wc2 == WATER_CLASS_CANAL) { /* Update infrastructure counts after the unconditional clear earlier. */ Company::Get(_current_company)->infrastructure.water += wc1 == WATER_CLASS_CANAL && wc2 == WATER_CLASS_CANAL ? 2 : 1; } Company::Get(_current_company)->infrastructure.water += 2 * LOCK_DEPOT_TILE_FACTOR; DirtyCompanyInfrastructureWindows(_current_company); assert(wc1 != WATER_CLASS_INVALID && wc2 != WATER_CLASS_INVALID); MakeShipDepot(tile, _current_company, depot->index, DEPOT_PART_NORTH, axis, wc1); MakeShipDepot(tile2, _current_company, depot->index, DEPOT_PART_SOUTH, axis, wc2); MarkTileDirtyByTile(tile); MarkTileDirtyByTile(tile2); MakeDefaultName(depot); } return cost; }
/** * Builds a lock. * @param tile Central tile of the lock. * @param dir Uphill direction. * @param flags Operation to perform. * @return The cost in case of success, or an error code if it failed. */ static CommandCost DoBuildLock(TileIndex tile, DiagDirection dir, DoCommandFlag flags) { CommandCost cost(EXPENSES_CONSTRUCTION); int delta = TileOffsByDiagDir(dir); CommandCost ret = EnsureNoVehicleOnGround(tile); if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta); if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta); if (ret.Failed()) return ret; /* middle tile */ WaterClass wc_middle = IsWaterTile(tile) ? GetWaterClass(tile) : WATER_CLASS_CANAL; ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; cost.AddCost(ret); /* lower tile */ WaterClass wc_lower = IsWaterTile(tile - delta) ? GetWaterClass(tile - delta) : WATER_CLASS_CANAL; if (!IsWaterTile(tile - delta)) { ret = DoCommand(tile - delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; cost.AddCost(ret); cost.AddCost(_price[PR_BUILD_CANAL]); } if (GetTileSlope(tile - delta) != SLOPE_FLAT) { return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); } /* upper tile */ WaterClass wc_upper = IsWaterTile(tile + delta) ? GetWaterClass(tile + delta) : WATER_CLASS_CANAL; if (!IsWaterTile(tile + delta)) { ret = DoCommand(tile + delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; cost.AddCost(ret); cost.AddCost(_price[PR_BUILD_CANAL]); } if (GetTileSlope(tile + delta) != SLOPE_FLAT) { return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); } if ((MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) || (MayHaveBridgeAbove(tile - delta) && IsBridgeAbove(tile - delta)) || (MayHaveBridgeAbove(tile + delta) && IsBridgeAbove(tile + delta))) { return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); } if (flags & DC_EXEC) { /* Update company infrastructure counts. */ Company *c = Company::GetIfValid(_current_company); if (c != NULL) { /* Counts for the water. */ if (!IsWaterTile(tile - delta)) c->infrastructure.water++; if (!IsWaterTile(tile + delta)) c->infrastructure.water++; /* Count for the lock itself. */ c->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // Lock is three tiles. DirtyCompanyInfrastructureWindows(_current_company); } MakeLock(tile, _current_company, dir, wc_lower, wc_upper, wc_middle); MarkTileDirtyByTile(tile); MarkTileDirtyByTile(tile - delta); MarkTileDirtyByTile(tile + delta); MarkCanalsAndRiversAroundDirty(tile - delta); MarkCanalsAndRiversAroundDirty(tile + delta); } cost.AddCost(_price[PR_BUILD_LOCK]); return cost; }
static uint32 StationGetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available) { const BaseStation *st = object->u.station.st; TileIndex tile = object->u.station.tile; if (object->scope == VSG_SCOPE_PARENT) { /* Pass the request on to the town of the station */ const Town *t; if (st != NULL) { t = st->town; } else if (tile != INVALID_TILE) { t = ClosestTownFromTile(tile, UINT_MAX); } else { *available = false; return UINT_MAX; } return TownGetVariable(variable, parameter, available, t); } if (st == NULL) { /* Station does not exist, so we're in a purchase list */ switch (variable) { case 0x40: case 0x41: case 0x46: case 0x47: case 0x49: return 0x2110000; // Platforms, tracks & position case 0x42: return 0; // Rail type (XXX Get current type from GUI?) case 0x43: return _current_company; // Station owner case 0x44: return 2; // PBS status case 0xFA: return Clamp(_date - DAYS_TILL_ORIGINAL_BASE_YEAR, 0, 65535); // Build date, clamped to a 16 bit value } *available = false; return UINT_MAX; } switch (variable) { /* Calculated station variables */ case 0x40: if (!HasBit(_svc.valid, 0)) { _svc.v40 = GetPlatformInfoHelper(tile, false, false, false); SetBit(_svc.valid, 0); } return _svc.v40; case 0x41: if (!HasBit(_svc.valid, 1)) { _svc.v41 = GetPlatformInfoHelper(tile, true, false, false); SetBit(_svc.valid, 1); } return _svc.v41; case 0x42: return GetTerrainType(tile) | (GetReverseRailTypeTranslation(GetRailType(tile), object->u.station.statspec->grf_prop.grffile) << 8); case 0x43: return st->owner; // Station owner case 0x44: return HasStationReservation(tile) ? 7 : 4; // PBS status case 0x45: if (!HasBit(_svc.valid, 2)) { _svc.v45 = GetRailContinuationInfo(tile); SetBit(_svc.valid, 2); } return _svc.v45; case 0x46: if (!HasBit(_svc.valid, 3)) { _svc.v46 = GetPlatformInfoHelper(tile, false, false, true); SetBit(_svc.valid, 3); } return _svc.v46; case 0x47: if (!HasBit(_svc.valid, 4)) { _svc.v47 = GetPlatformInfoHelper(tile, true, false, true); SetBit(_svc.valid, 4); } return _svc.v47; case 0x49: if (!HasBit(_svc.valid, 5)) { _svc.v49 = GetPlatformInfoHelper(tile, false, true, false); SetBit(_svc.valid, 5); } return _svc.v49; case 0x4A: // Animation frame of tile return GetAnimationFrame(tile); /* Variables which use the parameter */ /* Variables 0x60 to 0x65 are handled separately below */ case 0x66: // Animation frame of nearby tile if (parameter != 0) tile = GetNearbyTile(parameter, tile); return st->TileBelongsToRailStation(tile) ? GetAnimationFrame(tile) : UINT_MAX; case 0x67: { // Land info of nearby tile Axis axis = GetRailStationAxis(tile); if (parameter != 0) tile = GetNearbyTile(parameter, tile); // only perform if it is required Slope tileh = GetTileSlope(tile, NULL); bool swap = (axis == AXIS_Y && HasBit(tileh, CORNER_W) != HasBit(tileh, CORNER_E)); return GetNearbyTileInformation(tile) ^ (swap ? SLOPE_EW : 0); } case 0x68: { // Station info of nearby tiles TileIndex nearby_tile = GetNearbyTile(parameter, tile); if (!HasStationTileRail(nearby_tile)) return 0xFFFFFFFF; uint32 grfid = st->speclist[GetCustomStationSpecIndex(tile)].grfid; bool perpendicular = GetRailStationAxis(tile) != GetRailStationAxis(nearby_tile); bool same_station = st->TileBelongsToRailStation(nearby_tile); uint32 res = GB(GetStationGfx(nearby_tile), 1, 2) << 12 | !!perpendicular << 11 | !!same_station << 10; if (IsCustomStationSpecIndex(nearby_tile)) { const StationSpecList ssl = BaseStation::GetByTile(nearby_tile)->speclist[GetCustomStationSpecIndex(nearby_tile)]; res |= 1 << (ssl.grfid != grfid ? 9 : 8) | ssl.localidx; } return res; } /* General station variables */ case 0x82: return 50; case 0x84: return st->string_id; case 0x86: return 0; case 0xF0: return st->facilities; case 0xFA: return Clamp(st->build_date - DAYS_TILL_ORIGINAL_BASE_YEAR, 0, 65535); } return st->GetNewGRFVariable(object, variable, parameter, available); }
/* virtual */ uint32 StationScopeResolver::GetVariable(byte variable, uint32 parameter, bool *available) const { if (this->st == NULL) { /* Station does not exist, so we're in a purchase list or the land slope check callback. */ switch (variable) { case 0x40: case 0x41: case 0x46: case 0x47: case 0x49: return 0x2110000; // Platforms, tracks & position case 0x42: return 0; // Rail type (XXX Get current type from GUI?) case 0x43: return GetCompanyInfo(_current_company); // Station owner case 0x44: return 2; // PBS status case 0x67: // Land info of nearby tile if (this->axis != INVALID_AXIS && this->tile != INVALID_TILE) { TileIndex tile = this->tile; if (parameter != 0) tile = GetNearbyTile(parameter, tile, true, this->axis); // only perform if it is required Slope tileh = GetTileSlope(tile); bool swap = (this->axis == AXIS_Y && HasBit(tileh, CORNER_W) != HasBit(tileh, CORNER_E)); return GetNearbyTileInformation(tile, this->ro.grffile->grf_version >= 8) ^ (swap ? SLOPE_EW : 0); } break; case 0xFA: return Clamp(_date - DAYS_TILL_ORIGINAL_BASE_YEAR, 0, 65535); // Build date, clamped to a 16 bit value } *available = false; return UINT_MAX; } switch (variable) { /* Calculated station variables */ case 0x40: if (!HasBit(_svc.valid, 0)) { _svc.v40 = GetPlatformInfoHelper(this->tile, false, false, false); SetBit(_svc.valid, 0); } return _svc.v40; case 0x41: if (!HasBit(_svc.valid, 1)) { _svc.v41 = GetPlatformInfoHelper(this->tile, true, false, false); SetBit(_svc.valid, 1); } return _svc.v41; case 0x42: return GetTerrainType(this->tile) | (GetReverseRailTypeTranslation(GetRailType(this->tile), this->statspec->grf_prop.grffile) << 8); case 0x43: return GetCompanyInfo(this->st->owner); // Station owner case 0x44: return HasStationReservation(this->tile) ? 7 : 4; // PBS status case 0x45: if (!HasBit(_svc.valid, 2)) { _svc.v45 = GetRailContinuationInfo(this->tile); SetBit(_svc.valid, 2); } return _svc.v45; case 0x46: if (!HasBit(_svc.valid, 3)) { _svc.v46 = GetPlatformInfoHelper(this->tile, false, false, true); SetBit(_svc.valid, 3); } return _svc.v46; case 0x47: if (!HasBit(_svc.valid, 4)) { _svc.v47 = GetPlatformInfoHelper(this->tile, true, false, true); SetBit(_svc.valid, 4); } return _svc.v47; case 0x49: if (!HasBit(_svc.valid, 5)) { _svc.v49 = GetPlatformInfoHelper(this->tile, false, true, false); SetBit(_svc.valid, 5); } return _svc.v49; case 0x4A: // Animation frame of tile return GetAnimationFrame(this->tile); /* Variables which use the parameter */ /* Variables 0x60 to 0x65 and 0x69 are handled separately below */ case 0x66: { // Animation frame of nearby tile TileIndex tile = this->tile; if (parameter != 0) tile = GetNearbyTile(parameter, tile); return this->st->TileBelongsToRailStation(tile) ? GetAnimationFrame(tile) : UINT_MAX; } case 0x67: { // Land info of nearby tile Axis axis = GetRailStationAxis(this->tile); TileIndex tile = this->tile; if (parameter != 0) tile = GetNearbyTile(parameter, tile); // only perform if it is required Slope tileh = GetTileSlope(tile); bool swap = (axis == AXIS_Y && HasBit(tileh, CORNER_W) != HasBit(tileh, CORNER_E)); return GetNearbyTileInformation(tile, this->ro.grffile->grf_version >= 8) ^ (swap ? SLOPE_EW : 0); } case 0x68: { // Station info of nearby tiles TileIndex nearby_tile = GetNearbyTile(parameter, this->tile); if (!HasStationTileRail(nearby_tile)) return 0xFFFFFFFF; uint32 grfid = this->st->speclist[GetCustomStationSpecIndex(this->tile)].grfid; bool perpendicular = GetRailStationAxis(this->tile) != GetRailStationAxis(nearby_tile); bool same_station = this->st->TileBelongsToRailStation(nearby_tile); uint32 res = GB(GetStationGfx(nearby_tile), 1, 2) << 12 | !!perpendicular << 11 | !!same_station << 10; if (IsCustomStationSpecIndex(nearby_tile)) { const StationSpecList ssl = BaseStation::GetByTile(nearby_tile)->speclist[GetCustomStationSpecIndex(nearby_tile)]; res |= 1 << (ssl.grfid != grfid ? 9 : 8) | ssl.localidx; } return res; } /* General station variables */ case 0x82: return 50; case 0x84: return this->st->string_id; case 0x86: return 0; case 0xF0: return this->st->facilities; case 0xFA: return Clamp(this->st->build_date - DAYS_TILL_ORIGINAL_BASE_YEAR, 0, 65535); } return this->st->GetNewGRFVariable(this->ro, variable, parameter, available); }
static void TileLoop_Clear(TileIndex tile) { /* If the tile is at any edge flood it to prevent maps without water. */ if (_settings_game.construction.freeform_edges && DistanceFromEdge(tile) == 1) { uint z; Slope slope = GetTileSlope(tile, &z); if (z == 0 && slope == SLOPE_FLAT) { DoFloodTile(tile); MarkTileDirtyByTile(tile); return; } } TileLoopClearHelper(tile); switch (_settings_game.game_creation.landscape) { case LT_TROPIC: TileLoopClearDesert(tile); break; case LT_ARCTIC: TileLoopClearAlps(tile); break; } switch (GetClearGround(tile)) { case CLEAR_GRASS: if (GetClearDensity(tile) == 3) return; if (_game_mode != GM_EDITOR) { if (GetClearCounter(tile) < 7) { AddClearCounter(tile, 1); return; } else { SetClearCounter(tile, 0); AddClearDensity(tile, 1); } } else { SetClearGroundDensity(tile, GB(Random(), 0, 8) > 21 ? CLEAR_GRASS : CLEAR_ROUGH, 3); } break; case CLEAR_FIELDS: { uint field_type; if (_game_mode == GM_EDITOR) return; if (GetClearCounter(tile) < 7) { AddClearCounter(tile, 1); return; } else { SetClearCounter(tile, 0); } if (GetIndustryIndexOfField(tile) == INVALID_INDUSTRY && GetFieldType(tile) >= 7) { /* This farmfield is no longer farmfield, so make it grass again */ MakeClear(tile, CLEAR_GRASS, 2); } else { field_type = GetFieldType(tile); field_type = (field_type < 8) ? field_type + 1 : 0; SetFieldType(tile, field_type); } break; } default: return; } MarkTileDirtyByTile(tile); }
/** * Plant a tree. * @param tile start tile of area-drag of tree plantation * @param flags type of operation * @param p1 tree type, TREE_INVALID means random. * @param p2 end tile of area-drag * @param text unused * @return the cost of this operation or an error */ CommandCost CmdPlantTree(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { StringID msg = INVALID_STRING_ID; CommandCost cost(EXPENSES_OTHER); const byte tree_to_plant = GB(p1, 0, 8); // We cannot use Extract as min and max are climate specific. if (p2 >= MapSize()) return CMD_ERROR; /* Check the tree type within the current climate */ if (tree_to_plant != TREE_INVALID && !IsInsideBS(tree_to_plant, _tree_base_by_landscape[_settings_game.game_creation.landscape], _tree_count_by_landscape[_settings_game.game_creation.landscape])) return CMD_ERROR; TileArea ta(tile, p2); TILE_AREA_LOOP(tile, ta) { switch (GetTileType(tile)) { case MP_TREES: /* no more space for trees? */ if (_game_mode != GM_EDITOR && GetTreeCount(tile) == 4) { msg = STR_ERROR_TREE_ALREADY_HERE; continue; } if (flags & DC_EXEC) { AddTreeCount(tile, 1); MarkTileDirtyByTile(tile); } /* 2x as expensive to add more trees to an existing tile */ cost.AddCost(_price[PR_BUILD_TREES] * 2); break; case MP_WATER: if (!IsCoast(tile) || IsSlopeWithOneCornerRaised(GetTileSlope(tile, NULL))) { msg = STR_ERROR_CAN_T_BUILD_ON_WATER; continue; } /* FALL THROUGH */ case MP_CLEAR: { if (IsBridgeAbove(tile)) { msg = STR_ERROR_SITE_UNSUITABLE; continue; } TreeType treetype = (TreeType)tree_to_plant; /* Be a bit picky about which trees go where. */ if (_settings_game.game_creation.landscape == LT_TROPIC && treetype != TREE_INVALID && ( /* No cacti outside the desert */ (treetype == TREE_CACTUS && GetTropicZone(tile) != TROPICZONE_DESERT) || /* No rain forest trees outside the rain forest, except in the editor mode where it makes those tiles rain forest tile */ (IsInsideMM(treetype, TREE_RAINFOREST, TREE_CACTUS) && GetTropicZone(tile) != TROPICZONE_RAINFOREST && _game_mode != GM_EDITOR) || /* And no subtropical trees in the desert/rain forest */ (IsInsideMM(treetype, TREE_SUB_TROPICAL, TREE_TOYLAND) && GetTropicZone(tile) != TROPICZONE_NORMAL))) { msg = STR_ERROR_TREE_WRONG_TERRAIN_FOR_TREE_TYPE; continue; } if (IsTileType(tile, MP_CLEAR)) { /* Remove fields or rocks. Note that the ground will get barrened */ switch (GetRawClearGround(tile)) { case CLEAR_FIELDS: case CLEAR_ROCKS: { CommandCost ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; cost.AddCost(ret); break; } default: break; } } if (_game_mode != GM_EDITOR && Company::IsValidID(_current_company)) { Town *t = ClosestTownFromTile(tile, _settings_game.economy.dist_local_authority); if (t != NULL) ChangeTownRating(t, RATING_TREE_UP_STEP, RATING_TREE_MAXIMUM, flags); } if (flags & DC_EXEC) { if (treetype == TREE_INVALID) { treetype = GetRandomTreeType(tile, GB(Random(), 24, 8)); if (treetype == TREE_INVALID) treetype = TREE_CACTUS; } /* Plant full grown trees in scenario editor */ PlantTreesOnTile(tile, treetype, 0, _game_mode == GM_EDITOR ? 3 : 0); MarkTileDirtyByTile(tile); /* When planting rainforest-trees, set tropiczone to rainforest in editor. */ if (_game_mode == GM_EDITOR && IsInsideMM(treetype, TREE_RAINFOREST, TREE_CACTUS)) { SetTropicZone(tile, TROPICZONE_RAINFOREST); } } cost.AddCost(_price[PR_BUILD_TREES]); break; } default: msg = STR_ERROR_SITE_UNSUITABLE; break; } } if (cost.GetCost() == 0) { return_cmd_error(msg); } else { return cost; } }