inline int CurveCost(Trackdir td1, Trackdir td2) { assert(IsValidTrackdir(td1)); assert(IsValidTrackdir(td2)); int cost = 0; if (TrackFollower::Allow90degTurns() && ((TrackdirToTrackdirBits(td2) & (TrackdirBits)TrackdirCrossesTrackdirs(td1)) != 0)) { /* 90-deg curve penalty */ cost += Yapf().PfGetSettings().rail_curve90_penalty; } else if (td2 != NextTrackdir(td1)) { /* 45-deg curve penalty */ cost += Yapf().PfGetSettings().rail_curve45_penalty; } return cost; }
/** 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 */ FORCEINLINE bool PfCalcCost(Node& n, const TrackFollower *tf) { /* base tile cost depending on distance */ int c = IsDiagonalTrackdir(n.GetTrackdir()) ? YAPF_TILE_LENGTH : YAPF_TILE_CORNER_LENGTH; /* additional penalty for curves */ if (n.m_parent != NULL && n.GetTrackdir() != NextTrackdir(n.m_parent->GetTrackdir())) { /* new trackdir does not match the next one when going straight */ c += YAPF_TILE_LENGTH; } c += YAPF_TILE_LENGTH * tf->m_tiles_skipped; /* apply it */ n.m_cost = n.m_parent->m_cost + c; return true; }
static int32 NPFWaterPathCost(AyStar *as, AyStarNode *current, OpenListNode *parent) { /* TileIndex tile = current->tile; */ int32 cost = 0; Trackdir trackdir = current->direction; cost = _trackdir_length[trackdir]; // Should be different for diagonal tracks if (IsBuoyTile(current->tile) && IsDiagonalTrackdir(trackdir)) cost += _settings_game.pf.npf.npf_buoy_penalty; // A small penalty for going over buoys if (current->direction != NextTrackdir((Trackdir)parent->path.node.direction)) cost += _settings_game.pf.npf.npf_water_curve_penalty; /* @todo More penalties? */ return cost; }
/** * 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) { /* base tile cost depending on distance */ int c = IsDiagonalTrackdir(n.GetTrackdir()) ? YAPF_TILE_LENGTH : YAPF_TILE_CORNER_LENGTH; /* additional penalty for curves */ if (n.GetTrackdir() != NextTrackdir(n.m_parent->GetTrackdir())) { /* new trackdir does not match the next one when going straight */ c += YAPF_TILE_LENGTH; } /* Skipped tile cost for aqueducts. */ c += YAPF_TILE_LENGTH * tf->m_tiles_skipped; /* Ocean/canal speed penalty. */ const ShipVehicleInfo *svi = ShipVehInfo(Yapf().GetVehicle()->engine_type); byte speed_frac = (GetEffectiveWaterClass(n.GetTile()) == WATER_CLASS_SEA) ? svi->ocean_speed_frac : svi->canal_speed_frac; if (speed_frac > 0) c += YAPF_TILE_LENGTH * (1 + tf->m_tiles_skipped) * speed_frac / (256 - speed_frac); /* apply it */ n.m_cost = n.m_parent->m_cost + c; return true; }
/* 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; }