diff options
Diffstat (limited to 'src/trafficlight.cpp')
-rw-r--r-- | src/trafficlight.cpp | 291 |
1 files changed, 291 insertions, 0 deletions
diff --git a/src/trafficlight.cpp b/src/trafficlight.cpp new file mode 100644 index 000000000..4eeff8fe7 --- /dev/null +++ b/src/trafficlight.cpp @@ -0,0 +1,291 @@ +/* $Id: trafficlight.cpp $ */ + +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>. + */ + +/** @file trafficlight.cpp Handling of trafficlights. */ + +#include "stdafx.h" +#include "openttd.h" +#include "landscape.h" +#include "sprite.h" +#include "viewport_func.h" +#include "road_map.h" +#include "command_func.h" +#include "cheat_func.h" +#include "animated_tile_func.h" +#include "economy_func.h" +#include "road_cmd.h" +#include "company_func.h" +#include "company_base.h" +#include "settings_type.h" +#include "trafficlight.h" +#include "trafficlight_type.h" +#include "date_func.h" +#include "company_func.h" + +#include "table/sprites.h" +#include "table/strings.h" + +#include <set> + + +/* A traffic light consist (TLC) is a set of adjacent tiles with traffic lights on them. + * They are linked together to form a big traffic light junction. */ +typedef std::set<TileIndex> TLC; + +/** + * Gets the traffic light consist (a set of adjacent tiles with traffic lights). + * If specified by the checkroadworks parameter, it returns 0 instead if road works are found within the consist. + * @param tile Tile of the traffic light consist. + * @param checkroadworks Should we check for roadworks in the consist (and return 0 if found). + * @return visited The traffic light consist (TLC); if checkroadworks == true and roadworks were found, return 0 instead. + */ +TLC *GetTrafficLightConsist(TileIndex tile, bool checkroadworks) +{ + TLC *visited; + TLC *candidates; + + visited = new TLC; + candidates = new TLC; + candidates->insert(tile); + + while (!candidates->empty()) { + TLC::iterator cur = candidates->begin(); + if (checkroadworks && HasRoadWorks(*cur)) { + delete visited; + delete candidates; + return 0; + } + uint8 distance_between_traffic_lights = _tlc_distance[_settings_game.construction.max_tlc_distance]; + for (int i = 0; i < distance_between_traffic_lights; i++) { + TileIndex neighbor = *cur + ToTileIndexDiff(_tl_check_offsets[i]); + if (HasTrafficLights(neighbor) && (visited->find(neighbor) == visited->end())) candidates->insert(neighbor); + } + visited->insert(*cur); + candidates->erase(cur); + } + delete candidates; + return visited; +} + +/** + * Gets the lowest TileIndex of the traffic light consist or 0 if roadworks + * are found in the consist. + * @param tile Tile of the traffic light consist. + * @return lowest TileIndex in the consist or 0 if roadworks were found. + */ +TileIndex GetTLCLowestTileIndexOrRoadWorks(TileIndex tile) +{ + TLC *consist = GetTrafficLightConsist(tile, true); + TileIndex result = 0; + if (consist != 0) result = *(consist->begin()); + delete consist; + return result; +} + +/** + * Returns the state of the trafficlights on a tile. + * @note In the scenario editor trafficlights are disabled. + * @param tile This tile. + * @pre The tile must have trafficlights. + * @return Trafficlights state or disabled state. + */ +TrafficLightState GetTLState(TileIndex tile) +{ + assert(HasTrafficLights(tile)); + if (_game_mode == GM_EDITOR) return TLS_OFF; ///< All lights are off in scenario editor. + tile = GetTLCLowestTileIndexOrRoadWorks(tile); + if (tile == 0) return TLS_OFF; ///< All lights are off when roadworks are in the consist. + + uint16 tl_total = 16 * _settings_game.construction.traffic_lights_green_phase; // There are (16 * patchsetting) "TL ticks". + uint16 tl_tick = ((_tick_counter / 16) + 5 * TileX(tile) + 7 * TileY(tile)) % tl_total; // Each "TL tick" consists of 16 gameticks. + + if (tl_tick < ((tl_total / 2) - 2)) return TLS_X_GREEN_Y_RED; ///< SW and NE are green, NW and SE are red. + if (tl_tick < ((tl_total / 2) - 1)) return TLS_X_YELLOW_Y_RED; ///< SW and NE are yellow, NW and SE are red. + if (tl_tick < (tl_total / 2)) return TLS_X_RED_Y_REDYELLOW; ///< SW and NE are red, NW and SE are red-yellow. + if (tl_tick < (tl_total - 2)) return TLS_X_RED_Y_GREEN; ///< SW and NE are red, NW and SE are green. + if (tl_tick < (tl_total - 1)) return TLS_X_RED_Y_YELLOW; ///< SW and NE are red, NW and SE are yellow. + if (tl_tick < tl_total) return TLS_X_REDYELLOW_Y_RED; ///< SW and NE are red-yellow, NW and SE are red. + + NOT_REACHED(); + return TLS_OFF; +} + +/** + * Which directions are disallowed due to the TLState (red lights..). + */ +static const TrackdirBits _tls_to_trackdir[7] = { + TRACKDIR_BIT_MASK, ///< 0) all directions disallowed + TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_Y_SE | ///< 1) all directions from + TRACKDIR_BIT_UPPER_E | TRACKDIR_BIT_LOWER_W | ///< y sides + TRACKDIR_BIT_LEFT_S | TRACKDIR_BIT_RIGHT_N, ///< are disallowed + TRACKDIR_BIT_MASK, ///< 2) all directions disallowed + TRACKDIR_BIT_MASK, ///< 3) all directions disallowed + TRACKDIR_BIT_X_SW | TRACKDIR_BIT_X_NE | ///< 4) all directions from + TRACKDIR_BIT_UPPER_W | TRACKDIR_BIT_LOWER_E | ///< x sides + TRACKDIR_BIT_LEFT_N | TRACKDIR_BIT_RIGHT_S, ///< are disallowed + TRACKDIR_BIT_MASK, ///< 5) all directions disallowed + TRACKDIR_BIT_MASK, ///< 6) all directions disallowed +}; + +/** + * Which directions in tile are allowed to be taken due to adjacent traffic lights (traffic light consist). + * @param tile Tile to search on. + * @return trackdirbits Bitmask of allowed directions. + */ +TrackdirBits GetIntraTLCAllowedDirections(TileIndex tile) +{ + TrackdirBits trackdirbits = TRACKDIR_BIT_NONE; + + if (HasTrafficLights(tile + TileDiffXY( 1, 0))) // SW. + trackdirbits |= TRACKDIR_BIT_X_NE | TRACKDIR_BIT_LOWER_E | TRACKDIR_BIT_LEFT_N; + if (HasTrafficLights(tile + TileDiffXY( 0, 1))) // SE + trackdirbits |= TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_LOWER_W | TRACKDIR_BIT_RIGHT_N; + if (HasTrafficLights(tile + TileDiffXY( 0, -1))) // NW. + trackdirbits |= TRACKDIR_BIT_Y_SE | TRACKDIR_BIT_UPPER_E | TRACKDIR_BIT_LEFT_S; + if (HasTrafficLights(tile + TileDiffXY(-1, 0))) // NE. + trackdirbits |= TRACKDIR_BIT_X_SW | TRACKDIR_BIT_UPPER_W | TRACKDIR_BIT_RIGHT_S; + + return trackdirbits; +} + +/** + * Get a bitmask of the directions forbidden to drive on due to traffic light(s). + * @param tile Tile to check. + * @return Bitmask of forbidden directions. + */ +TrackdirBits GetTrafficLightDisallowedDirections(TileIndex tile) +{ + return (_tls_to_trackdir[GetTLState(tile)] & ~GetIntraTLCAllowedDirections(tile)); +} + +/** + * Checks if the size of a traffic light consist is within the allowed range. + * @param tile Tile to check (can also be a new tile to be added to the TLC). + * @return result True if the TLC size is within the allowed range, else false. + */ +bool CheckTLCSize(TileIndex tile) +{ + if (_settings_game.construction.max_tlc_size == 0) return true; + TLC *consist = GetTrafficLightConsist(tile, false); + bool result = (consist->size() <= _settings_game.construction.max_tlc_size); + delete consist; + return result; +} + +/** + * Build traffic lights on a crossing. + * @param tile Tile where to place the traffic lights. + * @param flags Operation to perform. + * @param p1 Unused. + * @param p2 Unused. + * @return CommandCost Cost or error. + */ +CommandCost CmdBuildTrafficLights(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + /* Check if traffic lights are enabled. */ + if (!_settings_game.construction.traffic_lights) return CMD_ERROR; // Sanity check. + + /* Check for correct location (road). */ + if (!IsTileType(tile, MP_ROAD) || GetRoadTileType(tile) != ROAD_TILE_NORMAL) return_cmd_error(STR_ERROR_THERE_IS_NO_ROAD); + + /* Check owner only if a valid player is executing this command. */ + if (Company::IsValidID(_current_company)) { + Owner owner = GetTileOwner(tile); + if (owner == OWNER_TOWN) { + if (!_settings_game.construction.allow_building_tls_in_towns) return_cmd_error(STR_ERROR_TRAFFIC_LIGHTS_NOT_ALLOWED_ON_TOWN_ROADS); + } else { + if (owner != OWNER_NONE && !IsTileOwner(tile, _current_company)) return_cmd_error(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER); // Owned by ... already displayed in CheckOwnership. + } + } + + /* Check junction and already built. */ + if (CountBits(GetAllRoadBits(tile)) < 3) return_cmd_error(STR_ERROR_CAN_ONLY_BE_PLACED_ON_ROAD_JUNCTIONS); + if (HasTrafficLights(tile)) return_cmd_error(STR_ERROR_ALREADY_BUILT); + + if (!CheckTLCSize(tile)) return_cmd_error(STR_ERROR_TRAFFIC_LIGHT_CONSIST_TOO_BIG); + + /* Now we may build the traffic lights. */ + if (flags & DC_EXEC) { + MakeTrafficLights(tile); + AddAnimatedTile(tile); + MarkTileDirtyByTile(tile); + } + return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS]); +} + +/** + * Removes traffic lights from a tile. + * @param tile Tile where to remove the traffic lights. + * @param flags Operation to perform. + * @param p1 Unused. + * @param p2 Unused. + * @return CommandCost Cost or error. + */ +CommandCost CmdRemoveTrafficLights(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +{ + /* Check for correct location (road with traffic lights). */ + if (!IsTileType(tile, MP_ROAD) || GetRoadTileType(tile) != ROAD_TILE_NORMAL || !HasTrafficLights(tile)) return CMD_ERROR; + + /* Check owner, but only if a valid player is executing this command. */ + if (Company::IsValidID(_current_company)) { + Owner owner = GetTileOwner(tile); + if (owner == OWNER_TOWN) { + if (!_settings_game.construction.allow_building_tls_in_towns && !_cheats.magic_bulldozer.value) return_cmd_error(STR_ERROR_TRAFFIC_LIGHTS_NOT_ALLOWED_ON_TOWN_ROADS); + } else { + if (owner != OWNER_NONE && !IsTileOwner(tile, _current_company)) return CMD_ERROR; // Owned by ... already displayed in CheckOwnership. + } + } + + /* Now we may remove the traffic lights. */ + if (flags & DC_EXEC) { + DeleteAnimatedTile(tile); + ClearTrafficLights(tile); + MarkTileDirtyByTile(tile); + } + return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS]); +} +/** + * Clear all traffic lights from the map. + */ +void ClearAllTrafficLights() +{ + /* Iterate over the whole map and remove any trafficlights found. */ + for (TileIndex tile = 0; tile < MapSize(); tile++) { + if (HasTrafficLights(tile)) { + DoCommand(tile, 0, 0, DC_EXEC, CMD_REMOVE_TRAFFICLIGHTS); + } + } +} + +/** + * Draws traffic lights on a tile. + * @param ti TileInfo of the tile to draw on. + */ +void DrawTrafficLights(TileInfo* ti) +{ + RoadBits road = GetAllRoadBits(ti->tile); + TrafficLightState state = GetTLState(ti->tile); + + const TileIndex neighbor[4] = { ti->tile + TileDiffXY(1, 0), // SW. + ti->tile + TileDiffXY(0, 1), // SE. + ti->tile + TileDiffXY(0, -1), // NW. + ti->tile + TileDiffXY(-1, 0)}; // NE. + const RoadBits rb[4] = {ROAD_SW, ROAD_SE, ROAD_NW, ROAD_NE}; + + /* Draw the four directions. */ + byte rs = _settings_game.vehicle.road_side; + for (int i = 0; i < 4; i++) { + if (road & rb[i] && !HasTrafficLights(neighbor[i])) { + DisallowedRoadDirections drd = DRD_NONE; + if (IsTileType(neighbor[i], MP_ROAD) && IsNormalRoad(neighbor[i])) drd = GetDisallowedRoadDirections(neighbor[i]); + if (drd != ((i % 2 == 0) ? DRD_SOUTHBOUND : DRD_NORTHBOUND) && drd != DRD_BOTH) + DrawRoadDetail(_tls_to_sprites[state][i], ti, _tl_offsets[rs][i].x, _tl_offsets[rs][i].y, 12); + } + } +} |