Example #1
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;
}
Example #2
0
/* Determine the cost of this node, for railway tracks */
static int32 NPFRailPathCost(AyStar *as, AyStarNode *current, OpenListNode *parent)
{
	TileIndex tile = current->tile;
	Trackdir trackdir = current->direction;
	int32 cost = 0;
	/* HACK: We create a OpenListNode manually, so we can call EndNodeCheck */
	OpenListNode new_node;

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

		case MP_RAILWAY:
			cost = _trackdir_length[trackdir]; // Should be different for diagonal tracks
			break;

		case MP_ROAD: // Railway crossing
			cost = NPF_TILE_LENGTH;
			break;

		case MP_STATION:
			/* We give a station tile a penalty. Logically we would only want to give
			 * station tiles that are not our destination this penalty. This would
			 * discourage trains to drive through busy stations. But, we can just
			 * give any station tile a penalty, because every possible route will get
			 * this penalty exactly once, on its end tile (if it's a station) and it
			 * will therefore not make a difference. */
			cost = NPF_TILE_LENGTH + _settings_game.pf.npf.npf_rail_station_penalty;

			if (IsRailWaypoint(tile)) {
				NPFFindStationOrTileData *fstd = (NPFFindStationOrTileData*)as->user_target;
				if (fstd->v->current_order.IsType(OT_GOTO_WAYPOINT) && GetStationIndex(tile) == fstd->v->current_order.GetDestination()) {
					/* 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. */
					const Train *train = Train::From(fstd->v);
					CFollowTrackRail ft(train);
					TileIndex t = tile;
					Trackdir td = trackdir;
					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(train, t, td, true, _settings_game.pf.forbid_90_deg)) break;
					}
					if (td == INVALID_TRACKDIR ||
							!IsSafeWaitingPosition(train, t, td, true, _settings_game.pf.forbid_90_deg) ||
							!IsWaitingPositionFree(train, t, td, _settings_game.pf.forbid_90_deg)) {
						cost += _settings_game.pf.npf.npf_rail_lastred_penalty;
					}
				}
			}
			break;

		default:
			break;
	}

	/* Determine extra costs */

	/* Check for signals */
	if (IsTileType(tile, MP_RAILWAY)) {
		if (HasSignalOnTrackdir(tile, trackdir)) {
			SignalType sigtype = GetSignalType(tile, TrackdirToTrack(trackdir));
			/* Ordinary track with signals */
			if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_RED) {
				/* Signal facing us is red */
				if (!NPFGetFlag(current, NPF_FLAG_SEEN_SIGNAL)) {
					/* Penalize the first signal we
					 * encounter, if it is red */

					/* Is this a presignal exit or combo? */
					if (!IsPbsSignal(sigtype)) {
						if (sigtype == SIGTYPE_EXIT || sigtype == SIGTYPE_COMBO) {
							/* Penalise exit and combo signals differently (heavier) */
							cost += _settings_game.pf.npf.npf_rail_firstred_exit_penalty;
						} else {
							cost += _settings_game.pf.npf.npf_rail_firstred_penalty;
						}
					}
				}
				/* Record the state of this signal */
				NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, true);
			} else {
				/* Record the state of this signal */
				NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, false);
			}
			if (NPFGetFlag(current, NPF_FLAG_SEEN_SIGNAL)) {
				if (NPFGetFlag(current, NPF_FLAG_2ND_SIGNAL)) {
					NPFSetFlag(current, NPF_FLAG_3RD_SIGNAL, true);
				} else {
					NPFSetFlag(current, NPF_FLAG_2ND_SIGNAL, true);
				}
			} else {
				NPFSetFlag(current, NPF_FLAG_SEEN_SIGNAL, true);
			}
			NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_BLOCK, !IsPbsSignal(sigtype));
		}

		if (HasPbsSignalOnTrackdir(tile, ReverseTrackdir(trackdir)) && !NPFGetFlag(current, NPF_FLAG_3RD_SIGNAL)) {
			cost += _settings_game.pf.npf.npf_rail_pbs_signal_back_penalty;
		}
	}

	/* Penalise the tile if it is a target tile and the last signal was
	 * red */
	/* HACK: We create a new_node here so we can call EndNodeCheck. Ugly as hell
	 * of course... */
	new_node.path.node = *current;
	if (as->EndNodeCheck(as, &new_node) == AYSTAR_FOUND_END_NODE && NPFGetFlag(current, NPF_FLAG_LAST_SIGNAL_RED))
		cost += _settings_game.pf.npf.npf_rail_lastred_penalty;

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

	/* Check for turns */
	if (current->direction != NextTrackdir((Trackdir)parent->path.node.direction))
		cost += _settings_game.pf.npf.npf_rail_curve_penalty;
	/* TODO, with realistic acceleration, also the amount of straight track between
	 *      curves should be taken into account, as this affects the speed limit. */

	/* Check for reverse in depot */
	if (IsRailDepotTile(tile) && as->EndNodeCheck(as, &new_node) != AYSTAR_FOUND_END_NODE) {
		/* Penalise any depot tile that is not the last tile in the path. This
		 * _should_ penalise every occurence of reversing in a depot (and only
		 * that) */
		cost += _settings_game.pf.npf.npf_rail_depot_reverse_penalty;
	}

	/* Check for occupied track */
	cost += NPFReservedTrackCost(current);

	NPFMarkTile(tile);
	DEBUG(npf, 4, "Calculating G for: (%d, %d). Result: %d", TileX(current->tile), TileY(current->tile), cost);
	return cost;
}