Пример #1
0
	/** main follower routine. Fills all members and return true on success.
	 *  Otherwise returns false if track can't be followed. */
	inline bool Follow(TileIndex old_tile, Trackdir old_td)
	{
		m_old_tile = old_tile;
		m_old_td = old_td;
		m_err = EC_NONE;
		assert(((TrackStatusToTrackdirBits(GetTileTrackStatus(m_old_tile, TT(), IsRoadTT() && m_veh != NULL ? RoadVehicle::From(m_veh)->compatible_roadtypes : 0)) & TrackdirToTrackdirBits(m_old_td)) != 0) ||
		       (IsTram() && GetSingleTramBit(m_old_tile) != INVALID_DIAGDIR)); // Disable the assertion for single tram bits
		m_exitdir = TrackdirToExitdir(m_old_td);
		if (ForcedReverse()) return true;
		if (!CanExitOldTile()) return false;
		FollowTileExit();
		if (!QueryNewTileTrackStatus()) return TryReverse();
		if (!CanEnterNewTile()) return false;
		m_new_td_bits &= DiagdirReachesTrackdirs(m_exitdir);
		if (m_new_td_bits == TRACKDIR_BIT_NONE) {
			m_err = EC_NO_WAY;
			return false;
		}
		if (!Allow90degTurns()) {
			m_new_td_bits &= (TrackdirBits)~(int)TrackdirCrossesTrackdirs(m_old_td);
			if (m_new_td_bits == TRACKDIR_BIT_NONE) {
				m_err = EC_90DEG;
				return false;
			}
		}
		return true;
	}
Пример #2
0
static uint NPFSlopeCost(AyStarNode *current)
{
	TileIndex next = current->tile + TileOffsByDiagDir(TrackdirToExitdir(current->direction));

	/* Get center of tiles */
	int x1 = TileX(current->tile) * TILE_SIZE + TILE_SIZE / 2;
	int y1 = TileY(current->tile) * TILE_SIZE + TILE_SIZE / 2;
	int x2 = TileX(next) * TILE_SIZE + TILE_SIZE / 2;
	int y2 = TileY(next) * TILE_SIZE + TILE_SIZE / 2;

	int dx4 = (x2 - x1) / 4;
	int dy4 = (y2 - y1) / 4;

	/* Get the height on both sides of the tile edge.
	 * Avoid testing the height on the tile-center. This will fail for halftile-foundations.
	 */
	int z1 = GetSlopeZ(x1 + dx4, y1 + dy4);
	int z2 = GetSlopeZ(x2 - dx4, y2 - dy4);

	if (z2 - z1 > 1) {
		/* Slope up */
		return _settings_game.pf.npf.npf_rail_slope_penalty;
	}
	return 0;
	/* Should we give a bonus for slope down? Probably not, we
	 * could just substract that bonus from the penalty, because
	 * there is only one level of steepness... */
}
Пример #3
0
	/** Check for a reserved station platform. */
	inline bool IsAnyStationTileReserved(TileIndex tile, Trackdir trackdir, int skipped)
	{
		TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(trackdir)));
		for (; skipped >= 0; skipped--, tile += diff) {
			if (HasStationReservation(tile)) return true;
		}
		return false;
	}
Пример #4
0
/* Determine the cost of this node, for road tracks */
static int32 NPFRoadPathCost(AyStar *as, AyStarNode *current, OpenListNode *parent)
{
	TileIndex tile = current->tile;
	int32 cost = 0;

	/* Determine base length */
	switch (GetTileType(tile)) {
		case MP_TUNNELBRIDGE:
			cost = IsTunnel(tile) ? NPFTunnelCost(current) : NPFBridgeCost(current);
			break;

		case MP_ROAD:
			cost = NPF_TILE_LENGTH;
			/* Increase the cost for level crossings */
			if (IsLevelCrossing(tile)) cost += _settings_game.pf.npf.npf_crossing_penalty;
			break;

		case MP_STATION: {
			cost = NPF_TILE_LENGTH;
			const RoadStop *rs = RoadStop::GetByTile(tile, GetRoadStopType(tile));
			if (IsDriveThroughStopTile(tile)) {
				/* Increase the cost for drive-through road stops */
				cost += _settings_game.pf.npf.npf_road_drive_through_penalty;
				DiagDirection dir = TrackdirToExitdir(current->direction);
				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. */
					const RoadStop::Entry *entry = rs->GetEntry(dir);
					cost += entry->GetOccupied() * _settings_game.pf.npf.npf_road_dt_occupied_penalty / entry->GetLength();
				}
			} else {
				/* Increase cost for filled road stops */
				cost += _settings_game.pf.npf.npf_road_bay_occupied_penalty * (!rs->IsFreeBay(0) + !rs->IsFreeBay(1)) / 2;
			}
			break;
		}

		default:
			break;
	}

	/* Determine extra costs */

	/* Check for slope */
	cost += NPFSlopeCost(current);

	/* Check for turns. Road vehicles only really drive diagonal, turns are
	 * represented by non-diagonal tracks */
	if (!IsDiagonalTrackdir(current->direction)) {
		cost += _settings_game.pf.npf.npf_road_curve_penalty;
	}

	NPFMarkTile(tile);
	DEBUG(npf, 4, "Calculating G for: (%d, %d). Result: %d", TileX(current->tile), TileY(current->tile), cost);
	return cost;
}
Пример #5
0
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);
}
Пример #6
0
/**
 * Lift the reservation of the tiles from @p start till @p end, excluding @p end itself.
 */
static void ClearPathReservation(const PathNode *start, const PathNode *end)
{
	bool first_run = true;
	for (; start != end; start = start->parent) {
		if (IsRailStationTile(start->node.tile) && first_run) {
			SetRailStationPlatformReservation(start->node.tile, TrackdirToExitdir(start->node.direction), false);
		} else {
			UnreserveRailTrack(start->node.tile, TrackdirToTrack(start->node.direction));
		}
		first_run = false;
	}
}
Пример #7
0
/* Will return the cost of the tunnel. If it is an entry, it will return the
 * cost of that tile. If the tile is an exit, it will return the tunnel length
 * including the exit tile. Requires that this is a Tunnel tile */
static uint NPFTunnelCost(AyStarNode *current)
{
	DiagDirection exitdir = TrackdirToExitdir(current->direction);
	TileIndex tile = current->tile;
	if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(exitdir)) {
		/* We just popped out if this tunnel, since were
		 * facing the tunnel exit */
		return NPF_TILE_LENGTH * (GetTunnelBridgeLength(current->tile, GetOtherTunnelEnd(current->tile)) + 1);
		/* @todo: Penalty for tunnels? */
	} else {
		/* We are entering the tunnel, the enter tile is just a
		 * straight track */
		return NPF_TILE_LENGTH;
	}
}
Пример #8
0
static uint NPFReservedTrackCost(AyStarNode *current)
{
	TileIndex tile = current->tile;
	TrackBits track = TrackToTrackBits(TrackdirToTrack(current->direction));
	TrackBits res = GetReservedTrackbits(tile);

	if (NPFGetFlag(current, NPF_FLAG_3RD_SIGNAL) || NPFGetFlag(current, NPF_FLAG_LAST_SIGNAL_BLOCK) || ((res & track) == TRACK_BIT_NONE && !TracksOverlap(res | track))) return 0;

	if (IsTileType(tile, MP_TUNNELBRIDGE)) {
		DiagDirection exitdir = TrackdirToExitdir(current->direction);
		if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(exitdir)) {
			return  _settings_game.pf.npf.npf_rail_pbs_cross_penalty * (GetTunnelBridgeLength(tile, GetOtherTunnelBridgeEnd(tile)) + 1);
		}
	}
	return  _settings_game.pf.npf.npf_rail_pbs_cross_penalty;
}
Пример #9
0
	/**
	 * main follower routine. Fills all members and return true on success.
	 *  Otherwise returns false if track can't be followed.
	 */
	inline bool Follow(TileIndex old_tile, Trackdir old_td)
	{
		m_old_tile = old_tile;
		m_old_td = old_td;
		m_err = EC_NONE;
		assert(
			((TrackStatusToTrackdirBits(
				GetTileTrackStatus(m_old_tile, TT(), (IsRoadTT() && m_veh != NULL) ? RoadVehicle::From(m_veh)->compatible_roadtypes : 0)
			) & TrackdirToTrackdirBits(m_old_td)) != 0) ||
			(IsTram() && GetSingleTramBit(m_old_tile) != INVALID_DIAGDIR) // Disable the assertion for single tram bits
		);
		m_exitdir = TrackdirToExitdir(m_old_td);
		if (ForcedReverse()) return true;
		if (!CanExitOldTile()) return false;
		FollowTileExit();
		if (!QueryNewTileTrackStatus()) return TryReverse();
		m_new_td_bits &= DiagdirReachesTrackdirs(m_exitdir);
		if (m_new_td_bits == TRACKDIR_BIT_NONE || !CanEnterNewTile()) {
			/* In case we can't enter the next tile, but are
			 * a normal road vehicle, then we can actually
			 * try to reverse as this is the end of the road.
			 * Trams can only turn on the appropriate bits in
			 * which case reaching this would mean a dead end
			 * near a building and in that case there would
			 * a "false" QueryNewTileTrackStatus result and
			 * as such reversing is already tried. The fact
			 * that function failed can have to do with a
			 * missing road bit, or inability to connect the
			 * different bits due to slopes. */
			if (IsRoadTT() && !IsTram() && TryReverse()) return true;

			/* CanEnterNewTile already set a reason.
			 * Do NOT overwrite it (important for example for EC_RAIL_TYPE).
			 * Only set a reason if CanEnterNewTile was not called */
			if (m_new_td_bits == TRACKDIR_BIT_NONE) m_err = EC_NO_WAY;

			return false;
		}
		if (!Allow90degTurns()) {
			m_new_td_bits &= (TrackdirBits)~(int)TrackdirCrossesTrackdirs(m_old_td);
			if (m_new_td_bits == TRACKDIR_BIT_NONE) {
				m_err = EC_90DEG;
				return false;
			}
		}
		return true;
	}
Пример #10
0
/**
 * Update signals around segment in _tbuset
 *
 * @param flags info about segment
 */
static void UpdateSignalsAroundSegment(SigFlags flags)
{
	TileIndex tile;
	Trackdir trackdir;

	while (_tbuset.Get(&tile, &trackdir)) {
		assert(HasSignalOnTrackdir(tile, trackdir));

		SignalType sig = GetSignalType(tile, TrackdirToTrack(trackdir));
		SignalState newstate = SIGNAL_STATE_GREEN;

		/* determine whether the new state is red */
		if (flags & SF_TRAIN) {
			/* train in the segment */
			newstate = SIGNAL_STATE_RED;
		} else {
			/* is it a bidir combo? - then do not count its other signal direction as exit */
			if (sig == SIGTYPE_COMBO && HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir))) {
				/* at least one more exit */
				if ((flags & SF_EXIT2) &&
						/* no green exit */
						(!(flags & SF_GREEN) ||
						/* only one green exit, and it is this one - so all other exits are red */
						(!(flags & SF_GREEN2) && GetSignalStateByTrackdir(tile, ReverseTrackdir(trackdir)) == SIGNAL_STATE_GREEN))) {
					newstate = SIGNAL_STATE_RED;
				}
			} else { // entry, at least one exit, no green exit
				if (IsPresignalEntry(tile, TrackdirToTrack(trackdir)) && (flags & SF_EXIT) && !(flags & SF_GREEN)) newstate = SIGNAL_STATE_RED;
			}
		}

		/* only when the state changes */
		if (newstate != GetSignalStateByTrackdir(tile, trackdir)) {
			if (IsPresignalExit(tile, TrackdirToTrack(trackdir))) {
				/* for pre-signal exits, add block to the global set */
				DiagDirection exitdir = TrackdirToExitdir(ReverseTrackdir(trackdir));
				_globset.Add(tile, exitdir); // do not check for full global set, first update all signals
			}
			SetSignalStateByTrackdir(tile, trackdir, newstate);
			MarkTileDirtyByTile(tile);
		}
	}

}
Пример #11
0
	/**
	 * 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;
	}
Пример #12
0
    /** return one tile cost */
    inline int OneTileCost(TileIndex tile, Trackdir trackdir)
    {
        int cost = 0;
        /* set base cost */
        if (IsDiagonalTrackdir(trackdir)) {
            cost += YAPF_TILE_LENGTH;
            switch (GetTileType(tile)) {
            case MP_ROAD:
                /* Increase the cost for level crossings */
                if (IsLevelCrossing(tile)) {
                    cost += Yapf().PfGetSettings().road_crossing_penalty;
                }
                break;

            case MP_STATION: {
                const RoadStop *rs = RoadStop::GetByTile(tile, GetRoadStopType(tile));
                if (IsDriveThroughStopTile(tile)) {
                    /* 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. */
                        const RoadStop::Entry *entry = rs->GetEntry(dir);
                        cost += entry->GetOccupied() * Yapf().PfGetSettings().road_stop_occupied_penalty / entry->GetLength();
                    }
                } else {
                    /* Increase cost for filled road stops */
                    cost += Yapf().PfGetSettings().road_stop_bay_occupied_penalty * (!rs->IsFreeBay(0) + !rs->IsFreeBay(1)) / 2;
                }
                break;
            }

            default:
                break;
            }
        } else {
            /* non-diagonal trackdir */
            cost = YAPF_TILE_CORNER_LENGTH + Yapf().PfGetSettings().road_curve_penalty;
        }
        return cost;
    }
Пример #13
0
/**
 * To be called when @p current contains the (shortest route to) the target node.
 * Will fill the contents of the NPFFoundTargetData using
 * AyStarNode[NPF_TRACKDIR_CHOICE]. If requested, path reservation
 * is done here.
 */
static void NPFSaveTargetData(AyStar *as, OpenListNode *current)
{
	NPFFoundTargetData *ftd = (NPFFoundTargetData*)as->user_path;
	ftd->best_trackdir = (Trackdir)current->path.node.user_data[NPF_TRACKDIR_CHOICE];
	ftd->best_path_dist = current->g;
	ftd->best_bird_dist = 0;
	ftd->node = current->path.node;
	ftd->res_okay = false;

	if (as->user_target != NULL && ((NPFFindStationOrTileData*)as->user_target)->reserve_path && as->user_data[NPF_TYPE] == TRANSPORT_RAIL) {
		/* Path reservation is requested. */
		const Train *v = Train::From(((NPFFindStationOrTileData *)as->user_target)->v);

		const PathNode *target = FindSafePosition(&current->path, v);
		ftd->node = target->node;

		/* If the target is a station skip to platform end. */
		if (IsRailStationTile(target->node.tile)) {
			DiagDirection dir = TrackdirToExitdir(target->node.direction);
			uint len = Station::GetByTile(target->node.tile)->GetPlatformLength(target->node.tile, dir);
			TileIndex end_tile = TILE_ADD(target->node.tile, (len - 1) * TileOffsByDiagDir(dir));

			/* Update only end tile, trackdir of a station stays the same. */
			ftd->node.tile = end_tile;
			if (!IsWaitingPositionFree(v, end_tile, target->node.direction, _settings_game.pf.forbid_90_deg)) return;
			SetRailStationPlatformReservation(target->node.tile, dir, true);
			SetRailStationReservation(target->node.tile, false);
		} else {
			if (!IsWaitingPositionFree(v, target->node.tile, target->node.direction, _settings_game.pf.forbid_90_deg)) return;
		}

		for (const PathNode *cur = target; cur->parent != NULL; cur = cur->parent) {
			if (!TryReserveRailTrack(cur->node.tile, TrackdirToTrack(cur->node.direction))) {
				/* Reservation failed, undo. */
				ClearPathReservation(target, cur);
				return;
			}
		}

		ftd->res_okay = true;
	}
}
Пример #14
0
    /**
     * Called by YAPF to calculate cost estimate. Calculates distance to the destination
     *  adds it to the actual cost from origin and stores the sum to the Node::m_estimate
     */
    inline bool PfCalcEstimate(Node& n)
    {
        static const int dg_dir_to_x_offs[] = {-1, 0, 1, 0};
        static const int dg_dir_to_y_offs[] = {0, 1, 0, -1};
        if (PfDetectDestination(n)) {
            n.m_estimate = n.m_cost;
            return true;
        }

        TileIndex tile = n.m_segment_last_tile;
        DiagDirection exitdir = TrackdirToExitdir(n.m_segment_last_td);
        int x1 = 2 * TileX(tile) + dg_dir_to_x_offs[(int)exitdir];
        int y1 = 2 * TileY(tile) + dg_dir_to_y_offs[(int)exitdir];
        int x2 = 2 * TileX(m_destTile);
        int y2 = 2 * TileY(m_destTile);
        int dx = abs(x1 - x2);
        int dy = abs(y1 - y2);
        int dmin = min(dx, dy);
        int dxy = abs(dx - dy);
        int d = dmin * YAPF_TILE_CORNER_LENGTH + (dxy - 1) * (YAPF_TILE_LENGTH / 2);
        n.m_estimate = n.m_cost + d;
        assert(n.m_estimate >= n.m_parent->m_estimate);
        return true;
    }
Пример #15
0
/* Will just follow the results of GetTileTrackStatus concerning where we can
 * go and where not. Uses AyStar.user_data[NPF_TYPE] as the transport type and
 * an argument to GetTileTrackStatus. Will skip tunnels, meaning that the
 * entry and exit are neighbours. Will fill
 * AyStarNode.user_data[NPF_TRACKDIR_CHOICE] with an appropriate value, and
 * copy AyStarNode.user_data[NPF_NODE_FLAGS] from the parent */
static void NPFFollowTrack(AyStar *aystar, OpenListNode *current)
{
	/* We leave src_tile on track src_trackdir in direction src_exitdir */
	Trackdir src_trackdir = current->path.node.direction;
	TileIndex src_tile = current->path.node.tile;
	DiagDirection src_exitdir = TrackdirToExitdir(src_trackdir);

	/* Is src_tile valid, and can be used?
	 * When choosing track on a junction src_tile is the tile neighboured to the junction wrt. exitdir.
	 * But we must not check the validity of this move, as src_tile is totally unrelated to the move, if a roadvehicle reversed on a junction. */
	bool ignore_src_tile = (current->path.parent == NULL && NPFGetFlag(&current->path.node, NPF_FLAG_IGNORE_START_TILE));

	/* Information about the vehicle: TransportType (road/rail/water) and SubType (compatible rail/road types) */
	TransportType type = (TransportType)aystar->user_data[NPF_TYPE];
	uint subtype = aystar->user_data[NPF_SUB_TYPE];

	/* Initialize to 0, so we can jump out (return) somewhere an have no neighbours */
	aystar->num_neighbours = 0;
	DEBUG(npf, 4, "Expanding: (%d, %d, %d) [%d]", TileX(src_tile), TileY(src_tile), src_trackdir, src_tile);

	/* We want to determine the tile we arrive, and which choices we have there */
	TileIndex dst_tile;
	TrackdirBits trackdirbits;

	/* Find dest tile */
	if (ignore_src_tile) {
		/* Do not perform any checks that involve src_tile */
		dst_tile = src_tile + TileOffsByDiagDir(src_exitdir);
		trackdirbits = GetDriveableTrackdirBits(dst_tile, src_trackdir, type, subtype);
	} else if (IsTileType(src_tile, MP_TUNNELBRIDGE) && GetTunnelBridgeDirection(src_tile) == src_exitdir) {
		/* We drive through the wormhole and arrive on the other side */
		dst_tile = GetOtherTunnelBridgeEnd(src_tile);
		trackdirbits = TrackdirToTrackdirBits(src_trackdir);
	} else if (ForceReverse(src_tile, src_exitdir, type, subtype)) {
		/* We can only reverse on this tile */
		dst_tile = src_tile;
		src_trackdir = ReverseTrackdir(src_trackdir);
		trackdirbits = TrackdirToTrackdirBits(src_trackdir);
	} else {
		/* We leave src_tile in src_exitdir and reach dst_tile */
		dst_tile = AddTileIndexDiffCWrap(src_tile, TileIndexDiffCByDiagDir(src_exitdir));

		if (dst_tile != INVALID_TILE && !CanEnterTile(dst_tile, src_exitdir, type, subtype, (RailTypes)aystar->user_data[NPF_RAILTYPES], (Owner)aystar->user_data[NPF_OWNER])) dst_tile = INVALID_TILE;

		if (dst_tile == INVALID_TILE) {
			/* We cannot enter the next tile. Road vehicles can reverse, others reach dead end */
			if (type != TRANSPORT_ROAD || HasBit(subtype, ROADTYPE_TRAM)) return;

			dst_tile = src_tile;
			src_trackdir = ReverseTrackdir(src_trackdir);
		}

		trackdirbits = GetDriveableTrackdirBits(dst_tile, src_trackdir, type, subtype);

		if (trackdirbits == 0) {
			/* We cannot enter the next tile. Road vehicles can reverse, others reach dead end */
			if (type != TRANSPORT_ROAD || HasBit(subtype, ROADTYPE_TRAM)) return;

			dst_tile = src_tile;
			src_trackdir = ReverseTrackdir(src_trackdir);

			trackdirbits = GetDriveableTrackdirBits(dst_tile, src_trackdir, type, subtype);
		}
	}

	if (NPFGetFlag(&current->path.node, NPF_FLAG_IGNORE_RESERVED)) {
		/* Mask out any reserved tracks. */
		TrackBits reserved = GetReservedTrackbits(dst_tile);
		trackdirbits &= ~TrackBitsToTrackdirBits(reserved);

		uint bits = TrackdirBitsToTrackBits(trackdirbits);
		int i;
		FOR_EACH_SET_BIT(i, bits) {
			if (TracksOverlap(reserved | TrackToTrackBits((Track)i))) trackdirbits &= ~TrackToTrackdirBits((Track)i);
		}
	}
Пример #16
0
	/**
	 * Called by YAPF to calculate the cost from the origin to the given node.
	 *  Calculates only the cost of given node, adds it to the parent node cost
	 *  and stores the result into Node::m_cost member
	 */
	inline bool PfCalcCost(Node &n, const TrackFollower *tf)
	{
		assert(!n.flags_u.flags_s.m_targed_seen);
		assert(tf->m_new_tile == n.m_key.m_tile);
		assert((TrackdirToTrackdirBits(n.m_key.m_td) & tf->m_new_td_bits) != TRACKDIR_BIT_NONE);

		CPerfStart perf_cost(Yapf().m_perf_cost);

		/* Does the node have some parent node? */
		bool has_parent = (n.m_parent != NULL);

		/* Do we already have a cached segment? */
		CachedData &segment = *n.m_segment;
		bool is_cached_segment = (segment.m_cost >= 0);

		int parent_cost = has_parent ? n.m_parent->m_cost : 0;

		/* Each node cost contains 2 or 3 main components:
		 *  1. Transition cost - cost of the move from previous node (tile):
		 *    - curve cost (or zero for straight move)
		 *  2. Tile cost:
		 *    - base tile cost
		 *      - YAPF_TILE_LENGTH for diagonal tiles
		 *      - YAPF_TILE_CORNER_LENGTH for non-diagonal tiles
		 *    - tile penalties
		 *      - tile slope penalty (upward slopes)
		 *      - red signal penalty
		 *      - level crossing penalty
		 *      - speed-limit penalty (bridges)
		 *      - station platform penalty
		 *      - penalty for reversing in the depot
		 *      - etc.
		 *  3. Extra cost (applies to the last node only)
		 *    - last red signal penalty
		 *    - penalty for too long or too short platform on the destination station
		 */
		int transition_cost = 0;
		int extra_cost = 0;

		/* Segment: one or more tiles connected by contiguous tracks of the same type.
		 * Each segment cost includes 'Tile cost' for all its tiles (including the first
		 * and last), and the 'Transition cost' between its tiles. The first transition
		 * cost of segment entry (move from the 'parent' node) is not included!
		 */
		int segment_entry_cost = 0;
		int segment_cost = 0;

		const Train *v = Yapf().GetVehicle();

		/* start at n.m_key.m_tile / n.m_key.m_td and walk to the end of segment */
		TILE cur(n.m_key.m_tile, n.m_key.m_td);

		/* the previous tile will be needed for transition cost calculations */
		TILE prev = !has_parent ? TILE() : TILE(n.m_parent->GetLastTile(), n.m_parent->GetLastTrackdir());

		EndSegmentReasonBits end_segment_reason = ESRB_NONE;

		TrackFollower tf_local(v, Yapf().GetCompatibleRailTypes(), &Yapf().m_perf_ts_cost);

		if (!has_parent) {
			/* We will jump to the middle of the cost calculator assuming that segment cache is not used. */
			assert(!is_cached_segment);
			/* Skip the first transition cost calculation. */
			goto no_entry_cost;
		}

		for (;;) {
			/* Transition cost (cost of the move from previous tile) */
			transition_cost = Yapf().CurveCost(prev.td, cur.td);
			transition_cost += Yapf().SwitchCost(prev.tile, cur.tile, TrackdirToExitdir(prev.td));

			/* First transition cost counts against segment entry cost, other transitions
			 * inside segment will come to segment cost (and will be cached) */
			if (segment_cost == 0) {
				/* We just entered the loop. First transition cost goes to segment entry cost)*/
				segment_entry_cost = transition_cost;
				transition_cost = 0;

				/* It is the right time now to look if we can reuse the cached segment cost. */
				if (is_cached_segment) {
					/* Yes, we already know the segment cost. */
					segment_cost = segment.m_cost;
					/* We know also the reason why the segment ends. */
					end_segment_reason = segment.m_end_segment_reason;
					/* We will need also some information about the last signal (if it was red). */
					if (segment.m_last_signal_tile != INVALID_TILE) {
						assert(HasSignalOnTrackdir(segment.m_last_signal_tile, segment.m_last_signal_td));
						SignalState sig_state = GetSignalStateByTrackdir(segment.m_last_signal_tile, segment.m_last_signal_td);
						bool is_red = (sig_state == SIGNAL_STATE_RED);
						n.flags_u.flags_s.m_last_signal_was_red = is_red;
						if (is_red) {
							n.m_last_red_signal_type = GetSignalType(segment.m_last_signal_tile, TrackdirToTrack(segment.m_last_signal_td));
						}
					}
					/* No further calculation needed. */
					cur = TILE(n.GetLastTile(), n.GetLastTrackdir());
					break;
				}
			} else {
				/* Other than first transition cost count as the regular segment cost. */
				segment_cost += transition_cost;
			}

no_entry_cost: // jump here at the beginning if the node has no parent (it is the first node)

			/* All other tile costs will be calculated here. */
			segment_cost += Yapf().OneTileCost(cur.tile, cur.td);

			/* If we skipped some tunnel/bridge/station tiles, add their base cost */
			segment_cost += YAPF_TILE_LENGTH * tf->m_tiles_skipped;

			/* Slope cost. */
			segment_cost += Yapf().SlopeCost(cur.tile, cur.td);

			/* Signal cost (routine can modify segment data). */
			segment_cost += Yapf().SignalCost(n, cur.tile, cur.td);

			/* Reserved tiles. */
			segment_cost += Yapf().ReservationCost(n, cur.tile, cur.td, tf->m_tiles_skipped);

			end_segment_reason = segment.m_end_segment_reason;

			/* Tests for 'potential target' reasons to close the segment. */
			if (cur.tile == prev.tile) {
				/* Penalty for reversing in a depot. */
				assert(IsRailDepot(cur.tile));
				segment_cost += Yapf().PfGetSettings().rail_depot_reverse_penalty;
				/* We will end in this pass (depot is possible target) */
				end_segment_reason |= ESRB_DEPOT;

			} else if (cur.tile_type == MP_STATION && IsRailWaypoint(cur.tile)) {
				if (v->current_order.IsType(OT_GOTO_WAYPOINT) &&
						GetStationIndex(cur.tile) == v->current_order.GetDestination() &&
						!Waypoint::Get(v->current_order.GetDestination())->IsSingleTile()) {
					/* This waypoint is our destination; maybe this isn't an unreserved
					 * one, so check that and if so see that as the last signal being
					 * red. This way waypoints near stations should work better. */
					CFollowTrackRail ft(v);
					TileIndex t = cur.tile;
					Trackdir td = cur.td;
					while (ft.Follow(t, td)) {
						assert(t != ft.m_new_tile);
						t = ft.m_new_tile;
						if (KillFirstBit(ft.m_new_td_bits) != TRACKDIR_BIT_NONE) {
							/* We encountered a junction; it's going to be too complex to
							 * handle this perfectly, so just bail out. There is no simple
							 * free path, so try the other possibilities. */
							td = INVALID_TRACKDIR;
							break;
						}
						td = RemoveFirstTrackdir(&ft.m_new_td_bits);
						/* If this is a safe waiting position we're done searching for it */
						if (IsSafeWaitingPosition(v, t, td, true, _settings_game.pf.forbid_90_deg)) break;
					}

					/* In the case this platform is (possibly) occupied we add penalty so the
					 * other platforms of this waypoint are evaluated as well, i.e. we assume
					 * that there is a red signal in the waypoint when it's occupied. */
					if (td == INVALID_TRACKDIR ||
							!IsSafeWaitingPosition(v, t, td, true, _settings_game.pf.forbid_90_deg) ||
							!IsWaitingPositionFree(v, t, td, _settings_game.pf.forbid_90_deg)) {
						extra_cost += Yapf().PfGetSettings().rail_lastred_penalty;
					}
				}
				/* Waypoint is also a good reason to finish. */
				end_segment_reason |= ESRB_WAYPOINT;

			} else if (tf->m_is_station) {
				/* Station penalties. */
				uint platform_length = tf->m_tiles_skipped + 1;
				/* We don't know yet if the station is our target or not. Act like
				 * if it is pass-through station (not our destination). */
				segment_cost += Yapf().PfGetSettings().rail_station_penalty * platform_length;
				/* We will end in this pass (station is possible target) */
				end_segment_reason |= ESRB_STATION;

			} else if (TrackFollower::DoTrackMasking() && cur.tile_type == MP_RAILWAY) {
				/* Searching for a safe tile? */
				if (HasSignalOnTrackdir(cur.tile, cur.td) && !IsPbsSignal(GetSignalType(cur.tile, TrackdirToTrack(cur.td)))) {
					end_segment_reason |= ESRB_SAFE_TILE;
				}
			}

			/* Apply min/max speed penalties only when inside the look-ahead radius. Otherwise
			 * it would cause desync in MP. */
			if (n.m_num_signals_passed < m_sig_look_ahead_costs.Size())
			{
				int min_speed = 0;
				int max_speed = tf->GetSpeedLimit(&min_speed);
				int max_veh_speed = v->GetDisplayMaxSpeed();
				if (max_speed < max_veh_speed) {
					extra_cost += YAPF_TILE_LENGTH * (max_veh_speed - max_speed) * (4 + tf->m_tiles_skipped) / max_veh_speed;
				}
				if (min_speed > max_veh_speed) {
					extra_cost += YAPF_TILE_LENGTH * (min_speed - max_veh_speed);
				}
			}

			/* Finish if we already exceeded the maximum path cost (i.e. when
			 * searching for the nearest depot). */
			if (m_max_cost > 0 && (parent_cost + segment_entry_cost + segment_cost) > m_max_cost) {
				end_segment_reason |= ESRB_PATH_TOO_LONG;
			}

			/* Move to the next tile/trackdir. */
			tf = &tf_local;
			tf_local.Init(v, Yapf().GetCompatibleRailTypes(), &Yapf().m_perf_ts_cost);

			if (!tf_local.Follow(cur.tile, cur.td)) {
				assert(tf_local.m_err != TrackFollower::EC_NONE);
				/* Can't move to the next tile (EOL?). */
				if (tf_local.m_err == TrackFollower::EC_RAIL_TYPE) {
					end_segment_reason |= ESRB_RAIL_TYPE;
				} else {
					end_segment_reason |= ESRB_DEAD_END;
				}

				if (TrackFollower::DoTrackMasking() && !HasOnewaySignalBlockingTrackdir(cur.tile, cur.td)) {
					end_segment_reason |= ESRB_SAFE_TILE;
				}
				break;
			}

			/* Check if the next tile is not a choice. */
			if (KillFirstBit(tf_local.m_new_td_bits) != TRACKDIR_BIT_NONE) {
				/* More than one segment will follow. Close this one. */
				end_segment_reason |= ESRB_CHOICE_FOLLOWS;
				break;
			}

			/* Gather the next tile/trackdir/tile_type/rail_type. */
			TILE next(tf_local.m_new_tile, (Trackdir)FindFirstBit2x64(tf_local.m_new_td_bits));

			if (TrackFollower::DoTrackMasking() && IsTileType(next.tile, MP_RAILWAY)) {
				if (HasSignalOnTrackdir(next.tile, next.td) && IsPbsSignal(GetSignalType(next.tile, TrackdirToTrack(next.td)))) {
					/* Possible safe tile. */
					end_segment_reason |= ESRB_SAFE_TILE;
				} else if (HasSignalOnTrackdir(next.tile, ReverseTrackdir(next.td)) && GetSignalType(next.tile, TrackdirToTrack(next.td)) == SIGTYPE_PBS_ONEWAY) {
					/* Possible safe tile, but not so good as it's the back of a signal... */
					end_segment_reason |= ESRB_SAFE_TILE | ESRB_DEAD_END;
					extra_cost += Yapf().PfGetSettings().rail_lastred_exit_penalty;
				}
			}

			/* Check the next tile for the rail type. */
			if (next.rail_type != cur.rail_type) {
				/* Segment must consist from the same rail_type tiles. */
				end_segment_reason |= ESRB_RAIL_TYPE;
				break;
			}

			/* Avoid infinite looping. */
			if (next.tile == n.m_key.m_tile && next.td == n.m_key.m_td) {
				end_segment_reason |= ESRB_INFINITE_LOOP;
				break;
			}

			if (segment_cost > s_max_segment_cost) {
				/* Potentially in the infinite loop (or only very long segment?). We should
				 * not force it to finish prematurely unless we are on a regular tile. */
				if (IsTileType(tf->m_new_tile, MP_RAILWAY)) {
					end_segment_reason |= ESRB_SEGMENT_TOO_LONG;
					break;
				}
			}

			/* Any other reason bit set? */
			if (end_segment_reason != ESRB_NONE) {
				break;
			}

			/* For the next loop set new prev and cur tile info. */
			prev = cur;
			cur = next;

		} // for (;;)

		bool target_seen = false;
		if ((end_segment_reason & ESRB_POSSIBLE_TARGET) != ESRB_NONE) {
			/* Depot, station or waypoint. */
			if (Yapf().PfDetectDestination(cur.tile, cur.td)) {
				/* Destination found. */
				target_seen = true;
			}
		}

		/* Update the segment if needed. */
		if (!is_cached_segment) {
			/* Write back the segment information so it can be reused the next time. */
			segment.m_cost = segment_cost;
			segment.m_end_segment_reason = end_segment_reason & ESRB_CACHED_MASK;
			/* Save end of segment back to the node. */
			n.SetLastTileTrackdir(cur.tile, cur.td);
		}

		/* Do we have an excuse why not to continue pathfinding in this direction? */
		if (!target_seen && (end_segment_reason & ESRB_ABORT_PF_MASK) != ESRB_NONE) {
			/* Reason to not continue. Stop this PF branch. */
			return false;
		}

		/* Special costs for the case we have reached our target. */
		if (target_seen) {
			n.flags_u.flags_s.m_targed_seen = true;
			/* Last-red and last-red-exit penalties. */
			if (n.flags_u.flags_s.m_last_signal_was_red) {
				if (n.m_last_red_signal_type == SIGTYPE_EXIT) {
					/* last signal was red pre-signal-exit */
					extra_cost += Yapf().PfGetSettings().rail_lastred_exit_penalty;
				} else if (!IsPbsSignal(n.m_last_red_signal_type)) {
					/* Last signal was red, but not exit or path signal. */
					extra_cost += Yapf().PfGetSettings().rail_lastred_penalty;
				}
			}

			/* Station platform-length penalty. */
			if ((end_segment_reason & ESRB_STATION) != ESRB_NONE) {
				const BaseStation *st = BaseStation::GetByTile(n.GetLastTile());
				assert(st != NULL);
				uint platform_length = st->GetPlatformLength(n.GetLastTile(), ReverseDiagDir(TrackdirToExitdir(n.GetLastTrackdir())));
				/* Reduce the extra cost caused by passing-station penalty (each station receives it in the segment cost). */
				extra_cost -= Yapf().PfGetSettings().rail_station_penalty * platform_length;
				/* Add penalty for the inappropriate platform length. */
				extra_cost += PlatformLengthPenalty(platform_length);
			}
		}

		/* total node cost */
		n.m_cost = parent_cost + segment_entry_cost + segment_cost + extra_cost;

		return true;
	}
Пример #17
0
	inline void Set(TileIndex tile, Trackdir td)
	{
		m_tile = tile;
		m_td = td;
		m_exitdir = (m_td == INVALID_TRACKDIR) ? INVALID_DIAGDIR : TrackdirToExitdir(m_td);
	}