diff --git a/src/pathfinder/npf/npf.cpp b/src/pathfinder/npf/npf.cpp index 5ac43cd8c9..cd27d77878 100644 --- a/src/pathfinder/npf/npf.cpp +++ b/src/pathfinder/npf/npf.cpp @@ -368,7 +368,7 @@ static int32_t NPFRoadPathCost(AyStar *, AyStarNode *current, OpenListNode *) /* When we're the first road stop in a 'queue' of them we increase * cost based on the fill percentage of the whole queue. */ const RoadStop::Entry *entry = rs->GetEntry(dir); - cost += entry->GetOccupied() * _settings_game.pf.npf.npf_road_dt_occupied_penalty / entry->GetLength(); + cost += entry->GetOccupied(rs, dir) * _settings_game.pf.npf.npf_road_dt_occupied_penalty / entry->GetLength(); } } else { /* Increase cost for filled road stops */ diff --git a/src/pathfinder/yapf/yapf_road.cpp b/src/pathfinder/yapf/yapf_road.cpp index 41102c2e15..4d07296f5e 100644 --- a/src/pathfinder/yapf/yapf_road.cpp +++ b/src/pathfinder/yapf/yapf_road.cpp @@ -75,11 +75,11 @@ protected: /* Increase the cost for drive-through road stops */ cost += Yapf().PfGetSettings().road_stop_penalty; DiagDirection dir = TrackdirToExitdir(trackdir); - if (!RoadStop::IsDriveThroughRoadStopContinuation(tile, tile - TileOffsByDiagDir(dir))) { - /* When we're the first road stop in a 'queue' of them we increase - * cost based on the fill percentage of the whole queue. */ + if (Yapf().PfDetectDestinationTile(tile, trackdir)){ + /* We've found the destination file so get the occupied + * level from the Entry object. */ const RoadStop::Entry *entry = rs->GetEntry(dir); - cost += entry->GetOccupied() * Yapf().PfGetSettings().road_stop_occupied_penalty / entry->GetLength(); + cost += entry->GetOccupied(rs, dir) * Yapf().PfGetSettings().road_stop_occupied_penalty / entry->GetLength(); } } else { /* Increase cost for filled road stops */ diff --git a/src/roadstop.cpp b/src/roadstop.cpp index 2e4f441973..93cdbc52fc 100644 --- a/src/roadstop.cpp +++ b/src/roadstop.cpp @@ -88,8 +88,6 @@ void RoadStop::MakeDriveThrough() if (south && rs_south->east != nullptr) { // (east != nullptr) == (west != nullptr) /* 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; @@ -175,12 +173,7 @@ void RoadStop::ClearDriveThrough() 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 maintenance 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. */ + /* We have to rebuild the entries to determine the length of the roadstop. */ rs_south_base->east->Rebuild(rs_south_base); rs_south_base->west->Rebuild(rs_south_base); @@ -219,9 +212,6 @@ void RoadStop::Leave(RoadVehicle *rv) /* Vehicle is leaving a road stop tile, mark bay as free */ this->FreeBay(HasBit(rv->state, RVS_USING_SECOND_BAY)); this->SetEntranceBusy(false); - } else { - /* Otherwise just leave the drive through's entry cache. */ - this->GetEntry(DirToDiagDir(rv->direction))->Leave(rv); } } @@ -248,9 +238,6 @@ bool RoadStop::Enter(RoadVehicle *rv) return true; } - /* Vehicles entering a drive-through stop from the 'normal' side use first bay (bay 0). */ - this->GetEntry(DirToDiagDir(rv->direction))->Enter(rv); - /* Indicate a drive-through stop */ SetBit(rv->state, RVS_IN_DT_ROAD_STOP); return true; @@ -273,28 +260,6 @@ bool RoadStop::Enter(RoadVehicle *rv) } } -/** - * Leave the road stop - * @param rv the vehicle that leaves the stop - */ -void RoadStop::Entry::Leave(const RoadVehicle *rv) -{ - this->occupied -= rv->gcache.cached_total_length; - assert(this->occupied >= 0); -} - -/** - * Enter the road stop - * @param rv the vehicle that enters the stop - */ -void RoadStop::Entry::Enter(const RoadVehicle *rv) -{ - /* we cannot assert on this->occupied < this->length because of the - * remote possibility that RVs are running through each other when - * trying to prevention an infinite jam. */ - this->occupied += rv->gcache.cached_total_length; -} - /** * Checks whether the 'next' tile is still part of the road same drive through * stop 'rs' in the same direction for the same vehicle. @@ -311,67 +276,88 @@ void RoadStop::Entry::Enter(const RoadVehicle *rv) IsDriveThroughStopTile(next); } -typedef std::list RVList; ///< A list of road vehicles - -/** Helper for finding RVs in a road stop. */ -struct RoadStopEntryRebuilderHelper { - RVList vehicles; ///< The list of vehicles to possibly add to. - DiagDirection dir; ///< The direction the vehicle has to face to be added. -}; - -/** - * Add road vehicles to the station's list if needed. - * @param v the found vehicle - * @param data the extra data used to make our decision - * @return always nullptr - */ -Vehicle *FindVehiclesInRoadStop(Vehicle *v, void *data) -{ - RoadStopEntryRebuilderHelper *rserh = (RoadStopEntryRebuilderHelper*)data; - /* Not a RV or not in the right direction or crashed :( */ - if (v->type != VEH_ROAD || DirToDiagDir(v->direction) != rserh->dir || !v->IsPrimaryVehicle() || (v->vehstatus & VS_CRASHED) != 0) return nullptr; - - RoadVehicle *rv = RoadVehicle::From(v); - /* Don't add ones not in a road stop */ - if (rv->state < RVSB_IN_ROAD_STOP) return nullptr; - - /* Do not add duplicates! */ - for (const auto &it : rserh->vehicles) { - if (rv == it) return nullptr; - } - - rserh->vehicles.push_back(rv); - return nullptr; -} - /** * 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) +void RoadStop::Entry::Rebuild(const RoadStop *rs) { 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 (const auto &it : rserh.vehicles) { - this->occupied += it->gcache.cached_total_length; } } +struct TileDataHelper +{ + TileIndex tile; ///< The tile we are working on. + DiagDirection dir; ///< The direction the tile is oriented following Trackdir. + uint free = TILE_SIZE; +}; + +/* Find the amount of free space in this tile, measured from the direction of entry. */ +Vehicle *FindTileFreeSpace(Vehicle *v, void *data) +{ + TileDataHelper *tdh = (TileDataHelper*)data; + + /* Exclude if not a RV, not travelling in the Trackdir direction */ + if (v->type != VEH_ROAD || DirToDiagDir(v->direction) != tdh->dir) return nullptr; + + RoadVehicle *rv = RoadVehicle::From(v); + + uint veh_pos = DiagDirToAxis(tdh->dir) == AXIS_X ? rv->x_pos : rv->y_pos; + uint tile_pos = DiagDirToAxis(tdh->dir) == AXIS_X ? TileX(tdh->tile) : TileY(tdh->tile); + uint tile_free_space = TILE_SIZE; + + switch (tdh->dir) { + case DIAGDIR_NE: + case DIAGDIR_NW: + tile_free_space = veh_pos % tile_pos < TILE_SIZE / 2 ? TILE_SIZE / 2 : 0; + tdh->free = std::min(tdh->free, tile_free_space); + break; + case DIAGDIR_SE: + case DIAGDIR_SW: + tile_free_space = veh_pos % tile_pos < TILE_SIZE / 2 ? 0 : TILE_SIZE / 2; + tdh->free = std::min(tdh->free, tile_free_space); + break; + default: + return nullptr; + } + return nullptr; +} + + +/** + * Get the amount of occupied space in this drive through stop. + * @param rs the roadstop this entry is part of + * @return the occupied space in tile units. + */ +int RoadStop::Entry::GetOccupied(const RoadStop *rs, const DiagDirection dir) const +{ + const RoadStop::Entry *entry = rs->GetEntry(dir); + + TileDataHelper tdh; + tdh.dir = dir; + + int free = 0; + TileIndexDiff offset = TileOffsByDiagDir(dir); + + for (TileIndex tile = rs->xy; RoadStop::IsDriveThroughRoadStopContinuation(rs->xy, tile); tile += offset) { + tdh.tile = tile; + FindVehicleOnPos(tile, &tdh, FindTileFreeSpace); + free += tdh.free; + + // Tile contains a vehicle - no need to keep calculating. + if (tdh.free < TILE_SIZE) break; + }; + + return entry->GetLength() - free; +} /** * Check the integrity of the data in this struct. @@ -385,6 +371,6 @@ void RoadStop::Entry::CheckIntegrity(const RoadStop *rs) const assert(!IsDriveThroughRoadStopContinuation(rs->xy, rs->xy - abs(TileOffsByDiagDir(GetRoadStopDir(rs->xy))))); Entry temp; - temp.Rebuild(rs, rs->east == this); - if (temp.length != this->length || temp.occupied != this->occupied) NOT_REACHED(); + temp.Rebuild(rs); + if (temp.length != this->length) NOT_REACHED(); } diff --git a/src/roadstop_base.h b/src/roadstop_base.h index 6bd3c268d8..9a9f4f6677 100644 --- a/src/roadstop_base.h +++ b/src/roadstop_base.h @@ -32,13 +32,12 @@ struct RoadStop : RoadStopPool::PoolItem<&_roadstop_pool> { struct Entry { private: int length; ///< The length of the stop in tile 'units' - int occupied; ///< The amount of occupied stop in tile 'units' public: friend struct RoadStop; ///< Oh yeah, the road stop may play with me. /** Create an entry */ - Entry() : length(0), occupied(0) {} + Entry() : length(0) {} /** * Get the length of this drive through stop. @@ -49,19 +48,9 @@ struct RoadStop : RoadStopPool::PoolItem<&_roadstop_pool> { return this->length; } - /** - * Get the amount of occupied space in this drive through stop. - * @return the occupied space in tile units. - */ - inline int GetOccupied() const - { - return this->occupied; - } - - void Leave(const RoadVehicle *rv); - void Enter(const RoadVehicle *rv); + int GetOccupied(const RoadStop *rs, const DiagDirection dir) const; void CheckIntegrity(const RoadStop *rs) const; - void Rebuild(const RoadStop *rs, int side = -1); + void Rebuild(const RoadStop *rs); }; TileIndex xy; ///< Position on the map