/** * Finds out if a given company's vehicles are allowed to enter a given tile. * @param owner The owner of the vehicle. * @param tile The tile that is about to be entered. * @param enterdir The direction in which the vehicle wants to enter the tile. * @return true if the vehicle can enter the tile. * @todo This function should be used in other places than just NPF, * maybe moved to another file too. */ static bool CanEnterTileOwnerCheck(Owner owner, TileIndex tile, DiagDirection enterdir) { if (IsTileType(tile, MP_RAILWAY) || // Rail tile (also rail depot) HasStationTileRail(tile) || // Rail station tile/waypoint IsRoadDepotTile(tile) || // Road depot tile IsStandardRoadStopTile(tile)) { // Road station tile (but not drive-through stops) return IsTileOwner(tile, owner); // You need to own these tiles entirely to use them } switch (GetTileType(tile)) { case MP_ROAD: /* rail-road crossing : are we looking at the railway part? */ if (IsLevelCrossing(tile) && DiagDirToAxis(enterdir) != GetCrossingRoadAxis(tile)) { return IsTileOwner(tile, owner); // Railway needs owner check, while the street is public } break; case MP_TUNNELBRIDGE: if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) { return IsTileOwner(tile, owner); } break; default: break; } return true; // no need to check }
static void TPFModeShip(TrackPathFinder *tpf, TileIndex tile, DiagDirection direction) { if (IsTileType(tile, MP_TUNNELBRIDGE)) { /* wrong track type */ if (GetTunnelBridgeTransportType(tile) != TRANSPORT_WATER) return; DiagDirection dir = GetTunnelBridgeDirection(tile); /* entering tunnel / bridge? */ if (dir == direction) { TileIndex endtile = GetOtherTunnelBridgeEnd(tile); tpf->rd.cur_length += GetTunnelBridgeLength(tile, endtile) + 1; tile = endtile; } else { /* leaving tunnel / bridge? */ if (ReverseDiagDir(dir) != direction) return; } } /* This addition will sometimes overflow by a single tile. * The use of TILE_MASK here makes sure that we still point at a valid * tile, and then this tile will be in the sentinel row/col, so GetTileTrackStatus will fail. */ tile = TILE_MASK(tile + TileOffsByDiagDir(direction)); if (++tpf->rd.cur_length > 50) return; TrackBits bits = TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0)) & DiagdirReachesTracks(direction); if (bits == TRACK_BIT_NONE) return; assert(TileX(tile) != MapMaxX() && TileY(tile) != MapMaxY()); bool only_one_track = true; do { Track track = RemoveFirstTrack(&bits); if (bits != TRACK_BIT_NONE) only_one_track = false; RememberData rd = tpf->rd; /* Change direction 4 times only */ if (!only_one_track && track != tpf->rd.last_choosen_track) { if (++tpf->rd.depth > 4) { tpf->rd = rd; return; } tpf->rd.last_choosen_track = track; } tpf->the_dir = TrackEnterdirToTrackdir(track, direction); if (!ShipTrackFollower(tile, tpf, tpf->rd.cur_length)) { TPFModeShip(tpf, tile, TrackdirToExitdir(tpf->the_dir)); } tpf->rd = rd; } while (bits != TRACK_BIT_NONE); }
/** * Determine the effective #WaterClass for a ship travelling on a tile. * @param tile Tile of interest * @return the waterclass to be used by the ship. */ WaterClass GetEffectiveWaterClass(TileIndex tile) { if (HasTileWaterClass(tile)) return GetWaterClass(tile); if (IsTileType(tile, MP_TUNNELBRIDGE)) { assert(GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER); return WATER_CLASS_CANAL; } if (IsTileType(tile, MP_RAILWAY)) { assert(GetRailGroundType(tile) == RAIL_GROUND_WATER); return WATER_CLASS_SEA; } NOT_REACHED(); }
void AfterLoadLabelMaps() { if (NeedRailTypeConversion()) { SmallVector<RailType, RAILTYPE_END> railtype_conversion_map; for (uint i = 0; i < _railtype_list.Length(); i++) { RailType r = GetRailTypeByLabel(_railtype_list[i]); if (r == INVALID_RAILTYPE) r = RAILTYPE_BEGIN; *railtype_conversion_map.Append() = r; } for (TileIndex t = 0; t < MapSize(); t++) { switch (GetTileType(t)) { case MP_RAILWAY: SetRailType(t, railtype_conversion_map[GetRailType(t)]); break; case MP_ROAD: if (IsLevelCrossing(t)) { SetRailType(t, railtype_conversion_map[GetRailType(t)]); } break; case MP_STATION: if (HasStationRail(t)) { SetRailType(t, railtype_conversion_map[GetRailType(t)]); } break; case MP_TUNNELBRIDGE: if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) { SetRailType(t, railtype_conversion_map[GetRailType(t)]); } break; default: break; } } } _railtype_list.Clear(); }
/** * Updates blocks in _globset buffer * * @param owner company whose signals we are updating * @return state of the first block from _globset * @pre Company::IsValidID(owner) */ static SigSegState UpdateSignalsInBuffer(Owner owner) { assert(Company::IsValidID(owner)); bool first = true; // first block? SigSegState state = SIGSEG_FREE; // value to return TileIndex tile; DiagDirection dir; while (_globset.Get(&tile, &dir)) { assert(_tbuset.IsEmpty()); assert(_tbdset.IsEmpty()); /* After updating signal, data stored are always MP_RAILWAY with signals. * Other situations happen when data are from outside functions - * modification of railbits (including both rail building and removal), * train entering/leaving block, train leaving depot... */ switch (GetTileType(tile)) { case MP_TUNNELBRIDGE: /* 'optimization assert' - do not try to update signals when it is not needed */ assert(GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL); assert(dir == INVALID_DIAGDIR || dir == ReverseDiagDir(GetTunnelBridgeDirection(tile))); _tbdset.Add(tile, INVALID_DIAGDIR); // we can safely start from wormhole centre _tbdset.Add(GetOtherTunnelBridgeEnd(tile), INVALID_DIAGDIR); break; case MP_RAILWAY: if (IsRailDepot(tile)) { /* 'optimization assert' do not try to update signals in other cases */ assert(dir == INVALID_DIAGDIR || dir == GetRailDepotDirection(tile)); _tbdset.Add(tile, INVALID_DIAGDIR); // start from depot inside break; } /* FALL THROUGH */ case MP_STATION: case MP_ROAD: if ((TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)) & _enterdir_to_trackbits[dir]) != TRACK_BIT_NONE) { /* only add to set when there is some 'interesting' track */ _tbdset.Add(tile, dir); _tbdset.Add(tile + TileOffsByDiagDir(dir), ReverseDiagDir(dir)); break; } /* FALL THROUGH */ default: /* jump to next tile */ tile = tile + TileOffsByDiagDir(dir); dir = ReverseDiagDir(dir); if ((TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0)) & _enterdir_to_trackbits[dir]) != TRACK_BIT_NONE) { _tbdset.Add(tile, dir); break; } /* happens when removing a rail that wasn't connected at one or both sides */ continue; // continue the while() loop } assert(!_tbdset.Overflowed()); // it really shouldn't overflow by these one or two items assert(!_tbdset.IsEmpty()); // it wouldn't hurt anyone, but shouldn't happen too SigFlags flags = ExploreSegment(owner); if (first) { first = false; /* SIGSEG_FREE is set by default */ if (flags & SF_PBS) { state = SIGSEG_PBS; } else if ((flags & SF_TRAIN) || ((flags & SF_EXIT) && !(flags & SF_GREEN)) || (flags & SF_FULL)) { state = SIGSEG_FULL; } } /* do not do anything when some buffer was full */ if (flags & SF_FULL) { ResetSets(); // free all sets break; } UpdateSignalsAroundSegment(flags); } return state; }
/** * Search signal block * * @param owner owner whose signals we are updating * @return SigFlags */ static SigFlags ExploreSegment(Owner owner) { SigFlags flags = SF_NONE; TileIndex tile; DiagDirection enterdir; while (_tbdset.Get(&tile, &enterdir)) { TileIndex oldtile = tile; // tile we are leaving DiagDirection exitdir = enterdir == INVALID_DIAGDIR ? INVALID_DIAGDIR : ReverseDiagDir(enterdir); // expected new exit direction (for straight line) switch (GetTileType(tile)) { case MP_RAILWAY: { if (GetTileOwner(tile) != owner) continue; // do not propagate signals on others' tiles (remove for tracksharing) if (IsRailDepot(tile)) { if (enterdir == INVALID_DIAGDIR) { // from 'inside' - train just entered or left the depot if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; exitdir = GetRailDepotDirection(tile); tile += TileOffsByDiagDir(exitdir); enterdir = ReverseDiagDir(exitdir); break; } else if (enterdir == GetRailDepotDirection(tile)) { // entered a depot if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; continue; } else { continue; } } TrackBits tracks = GetTrackBits(tile); // trackbits of tile TrackBits tracks_masked = (TrackBits)(tracks & _enterdir_to_trackbits[enterdir]); // only incidating trackbits if (tracks == TRACK_BIT_HORZ || tracks == TRACK_BIT_VERT) { // there is exactly one incidating track, no need to check tracks = tracks_masked; /* If no train detected yet, and there is not no train -> there is a train -> set the flag */ if (!(flags & SF_TRAIN) && EnsureNoTrainOnTrackBits(tile, tracks).Failed()) flags |= SF_TRAIN; } else { if (tracks_masked == TRACK_BIT_NONE) continue; // no incidating track if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; } if (HasSignals(tile)) { // there is exactly one track - not zero, because there is exit from this tile Track track = TrackBitsToTrack(tracks_masked); // mask TRACK_BIT_X and Y too if (HasSignalOnTrack(tile, track)) { // now check whole track, not trackdir SignalType sig = GetSignalType(tile, track); Trackdir trackdir = (Trackdir)FindFirstBit((tracks * 0x101) & _enterdir_to_trackdirbits[enterdir]); Trackdir reversedir = ReverseTrackdir(trackdir); /* add (tile, reversetrackdir) to 'to-be-updated' set when there is * ANY conventional signal in REVERSE direction * (if it is a presignal EXIT and it changes, it will be added to 'to-be-done' set later) */ if (HasSignalOnTrackdir(tile, reversedir)) { if (IsPbsSignal(sig)) { flags |= SF_PBS; } else if (!_tbuset.Add(tile, reversedir)) { return flags | SF_FULL; } } if (HasSignalOnTrackdir(tile, trackdir) && !IsOnewaySignal(tile, track)) flags |= SF_PBS; /* if it is a presignal EXIT in OUR direction and we haven't found 2 green exits yes, do special check */ if (!(flags & SF_GREEN2) && IsPresignalExit(tile, track) && HasSignalOnTrackdir(tile, trackdir)) { // found presignal exit if (flags & SF_EXIT) flags |= SF_EXIT2; // found two (or more) exits flags |= SF_EXIT; // found at least one exit - allow for compiler optimizations if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_GREEN) { // found green presignal exit if (flags & SF_GREEN) flags |= SF_GREEN2; flags |= SF_GREEN; } } continue; } } for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) { // test all possible exit directions if (dir != enterdir && (tracks & _enterdir_to_trackbits[dir])) { // any track incidating? TileIndex newtile = tile + TileOffsByDiagDir(dir); // new tile to check DiagDirection newdir = ReverseDiagDir(dir); // direction we are entering from if (!MaybeAddToTodoSet(newtile, newdir, tile, dir)) return flags | SF_FULL; } } continue; // continue the while() loop } case MP_STATION: if (!HasStationRail(tile)) continue; if (GetTileOwner(tile) != owner) continue; if (DiagDirToAxis(enterdir) != GetRailStationAxis(tile)) continue; // different axis if (IsStationTileBlocked(tile)) continue; // 'eye-candy' station tile if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; tile += TileOffsByDiagDir(exitdir); break; case MP_ROAD: if (!IsLevelCrossing(tile)) continue; if (GetTileOwner(tile) != owner) continue; if (DiagDirToAxis(enterdir) == GetCrossingRoadAxis(tile)) continue; // different axis if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; tile += TileOffsByDiagDir(exitdir); break; case MP_TUNNELBRIDGE: { if (GetTileOwner(tile) != owner) continue; if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue; DiagDirection dir = GetTunnelBridgeDirection(tile); if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; enterdir = dir; exitdir = ReverseDiagDir(dir); tile += TileOffsByDiagDir(exitdir); // just skip to next tile } else { // NOT incoming from the wormhole! if (ReverseDiagDir(enterdir) != dir) continue; if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; tile = GetOtherTunnelBridgeEnd(tile); // just skip to exit tile enterdir = INVALID_DIAGDIR; exitdir = INVALID_DIAGDIR; } } break; default: continue; // continue the while() loop } if (!MaybeAddToTodoSet(tile, enterdir, oldtile, exitdir)) return flags | SF_FULL; } return flags; }