/** * Tests if a tile can be entered or left only from one side. * * Depots, non-drive-through roadstops, and tiles with single trambits are tested. * * @param tile The tile of interest. * @param type The transporttype of the vehicle. * @param subtype For TRANSPORT_ROAD the compatible RoadTypes of the vehicle. * @return The single entry/exit-direction of the tile, or INVALID_DIAGDIR if there are more or less directions */ static DiagDirection GetTileSingleEntry(TileIndex tile, TransportType type, uint subtype) { if (type != TRANSPORT_WATER && IsDepotTypeTile(tile, type)) return GetDepotDirection(tile, type); if (type == TRANSPORT_ROAD) { if (IsStandardRoadStopTile(tile)) return GetRoadStopDir(tile); if (HasBit(subtype, ROADTYPE_TRAM)) return GetSingleTramBit(tile); } return INVALID_DIAGDIR; }
/** * Rebuild, from scratch, the vehicles and other metadata on this stop. * @param rs the roadstop this entry is part of * @param side the side of the road stop to look at */ void RoadStop::Entry::Rebuild(const RoadStop *rs, int side) { assert(HasBit(rs->status, RSSFB_BASE_ENTRY)); DiagDirection dir = GetRoadStopDir(rs->xy); if (side == -1) side = (rs->east == this); RoadStopEntryRebuilderHelper rserh; rserh.dir = side ? dir : ReverseDiagDir(dir); this->length = 0; TileIndexDiff offset = abs(TileOffsByDiagDir(dir)); for (TileIndex tile = rs->xy; IsDriveThroughRoadStopContinuation(rs->xy, tile); tile += offset) { this->length += TILE_SIZE; FindVehicleOnPos(tile, &rserh, FindVehiclesInRoadStop); } this->occupied = 0; for (RVList::iterator it = rserh.vehicles.begin(); it != rserh.vehicles.end(); it++) { this->occupied += (*it)->gcache.cached_total_length; } }
/** * Join this road stop to another 'base' road stop if possible; * fill all necessary data to become an actual drive through road stop. * Also update the length etc. */ void RoadStop::MakeDriveThrough() { assert(this->east == NULL && this->west == NULL); RoadStopType rst = GetRoadStopType(this->xy); DiagDirection dir = GetRoadStopDir(this->xy); /* Use absolute so we always go towards the nortern tile */ TileIndexDiff offset = abs(TileOffsByDiagDir(dir)); /* Information about the tile north of us */ TileIndex north_tile = this->xy - offset; bool north = IsDriveThroughRoadStopContinuation(this->xy, north_tile); RoadStop *rs_north = north ? RoadStop::GetByTile(north_tile, rst) : NULL; /* Information about the tile south of us */ TileIndex south_tile = this->xy + offset; bool south = IsDriveThroughRoadStopContinuation(this->xy, south_tile); RoadStop *rs_south = south ? RoadStop::GetByTile(south_tile, rst) : NULL; /* Amount of road stops that will be added to the 'northern' head */ int added = 1; if (north && rs_north->east != NULL) { // (east != NULL) == (west != NULL) /* There is a more nothern one, so this can join them */ this->east = rs_north->east; this->west = rs_north->west; if (south && rs_south->east != NULL) { // (east != NULL) == (west != NULL) /* There more southern tiles too, they must 'join' us too */ ClrBit(rs_south->status, RSSFB_BASE_ENTRY); this->east->occupied += rs_south->east->occupied; this->west->occupied += rs_south->west->occupied; /* Free the now unneeded entry structs */ delete rs_south->east; delete rs_south->west; /* Make all 'children' of the southern tile take the new master */ for (; IsDriveThroughRoadStopContinuation(this->xy, south_tile); south_tile += offset) { rs_south = RoadStop::GetByTile(south_tile, rst); if (rs_south->east == NULL) break; rs_south->east = rs_north->east; rs_south->west = rs_north->west; added++; } } } else if (south && rs_south->east != NULL) { // (east != NULL) == (west != NULL) /* There is one to the south, but not to the north... so we become 'parent' */ this->east = rs_south->east; this->west = rs_south->west; SetBit(this->status, RSSFB_BASE_ENTRY); ClrBit(rs_south->status, RSSFB_BASE_ENTRY); } else { /* We are the only... so we are automatically the master */ this->east = new Entry(); this->west = new Entry(); SetBit(this->status, RSSFB_BASE_ENTRY); } /* Now update the lengths */ added *= TILE_SIZE; this->east->length += added; this->west->length += added; }
/** * Prepare for removal of this stop; update other neighbouring stops * if needed. Also update the length etc. */ void RoadStop::ClearDriveThrough() { assert(this->east != NULL && this->west != NULL); RoadStopType rst = GetRoadStopType(this->xy); DiagDirection dir = GetRoadStopDir(this->xy); /* Use absolute so we always go towards the nortern tile */ TileIndexDiff offset = abs(TileOffsByDiagDir(dir)); /* Information about the tile north of us */ TileIndex north_tile = this->xy - offset; bool north = IsDriveThroughRoadStopContinuation(this->xy, north_tile); RoadStop *rs_north = north ? RoadStop::GetByTile(north_tile, rst) : NULL; /* Information about the tile south of us */ TileIndex south_tile = this->xy + offset; bool south = IsDriveThroughRoadStopContinuation(this->xy, south_tile); RoadStop *rs_south = south ? RoadStop::GetByTile(south_tile, rst) : NULL; /* Must only be cleared after we determined which neighbours are * part of our little entry 'queue' */ DoClearSquare(this->xy); if (north) { /* There is a tile to the north, so we can't clear ourselves. */ if (south) { /* There are more southern tiles too, they must be split; * first make the new southern 'base' */ SetBit(rs_south->status, RSSFB_BASE_ENTRY); rs_south->east = new Entry(); rs_south->west = new Entry(); /* Keep track of the base because we need it later on */ RoadStop *rs_south_base = rs_south; TileIndex base_tile = south_tile; /* Make all (even more) southern stops part of the new entry queue */ for (south_tile += offset; IsDriveThroughRoadStopContinuation(base_tile, south_tile); south_tile += offset) { rs_south = RoadStop::GetByTile(south_tile, rst); rs_south->east = rs_south_base->east; rs_south->west = rs_south_base->west; } /* Find the other end; the northern most tile */ for (; IsDriveThroughRoadStopContinuation(base_tile, north_tile); north_tile -= offset) { rs_north = RoadStop::GetByTile(north_tile, rst); } /* We have to rebuild the entries because we cannot easily determine * how full each part is. So instead of keeping and maintaining a list * of vehicles and using that to 'rebuild' the occupied state we just * rebuild it from scratch as that removes lots of maintainance code * for the vehicle list and it's faster in real games as long as you * do not keep split and merge road stop every tick by the millions. */ rs_south_base->east->Rebuild(rs_south_base); rs_south_base->west->Rebuild(rs_south_base); assert(HasBit(rs_north->status, RSSFB_BASE_ENTRY)); rs_north->east->Rebuild(rs_north); rs_north->west->Rebuild(rs_north); } else { /* Only we left, so simple update the length. */ rs_north->east->length -= TILE_SIZE; rs_north->west->length -= TILE_SIZE; } } else if (south) { /* There is only something to the south. Hand over the base entry */ SetBit(rs_south->status, RSSFB_BASE_ENTRY); rs_south->east->length -= TILE_SIZE; rs_south->west->length -= TILE_SIZE; } else { /* We were the last */ delete this->east; delete this->west; } /* Make sure we don't get used for something 'incorrect' */ ClrBit(this->status, RSSFB_BASE_ENTRY); this->east = NULL; this->west = NULL; }