static void CheckShipLeaveDepot(Ship *v) { if (!v->IsInDepot()) return; TileIndex tile = v->tile; Axis axis = GetShipDepotAxis(tile); /* Check first (north) side */ if (DiagdirReachesTracks((DiagDirection)axis) & GetTileShipTrackStatus(TILE_ADD(tile, ToTileIndexDiff(_ship_leave_depot_offs[axis])))) { v->direction = ReverseDir(AxisToDirection(axis)); /* Check second (south) side */ } else if (DiagdirReachesTracks((DiagDirection)(axis + 2)) & GetTileShipTrackStatus(TILE_ADD(tile, -2 * ToTileIndexDiff(_ship_leave_depot_offs[axis])))) { v->direction = AxisToDirection(axis); } else { return; } v->state = AxisToTrackBits(axis); v->vehstatus &= ~VS_HIDDEN; v->cur_speed = 0; RecalcShipStuff(v); PlayShipSound(v); VehicleServiceInDepot(v); InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); SetWindowClassesDirty(WC_SHIPS_LIST); }
static bool CheckShipLeaveDepot(Ship *v) { if (!v->IsChainInDepot()) return false; /* We are leaving a depot, but have to go to the exact same one; re-enter */ if (v->current_order.IsType(OT_GOTO_DEPOT) && IsShipDepotTile(v->tile) && GetDepotIndex(v->tile) == v->current_order.GetDestination()) { VehicleEnterDepot(v); return true; } TileIndex tile = v->tile; Axis axis = GetShipDepotAxis(tile); DiagDirection north_dir = ReverseDiagDir(AxisToDiagDir(axis)); TileIndex north_neighbour = TILE_ADD(tile, TileOffsByDiagDir(north_dir)); DiagDirection south_dir = AxisToDiagDir(axis); TileIndex south_neighbour = TILE_ADD(tile, 2 * TileOffsByDiagDir(south_dir)); TrackBits north_tracks = DiagdirReachesTracks(north_dir) & GetTileShipTrackStatus(north_neighbour); TrackBits south_tracks = DiagdirReachesTracks(south_dir) & GetTileShipTrackStatus(south_neighbour); if (north_tracks && south_tracks) { /* Ask pathfinder for best direction */ bool reverse = false; bool path_found; switch (_settings_game.pf.pathfinder_for_ships) { case VPF_OPF: reverse = OPFShipChooseTrack(v, north_neighbour, north_dir, north_tracks, path_found) == INVALID_TRACK; break; // OPF always allows reversing case VPF_NPF: reverse = NPFShipCheckReverse(v); break; case VPF_YAPF: reverse = YapfShipCheckReverse(v); break; default: NOT_REACHED(); } if (reverse) north_tracks = TRACK_BIT_NONE; } if (north_tracks) { /* Leave towards north */ v->direction = DiagDirToDir(north_dir); } else if (south_tracks) { /* Leave towards south */ v->direction = DiagDirToDir(south_dir); } else { /* Both ways blocked */ return false; } v->state = AxisToTrackBits(axis); v->vehstatus &= ~VS_HIDDEN; v->cur_speed = 0; v->UpdateViewport(true, true); SetWindowDirty(WC_VEHICLE_DEPOT, v->tile); PlayShipSound(v); VehicleServiceInDepot(v); InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); SetWindowClassesDirty(WC_SHIPS_LIST); return false; }
/** * Gets the other end of the aqueduct, if possible. * @param tile_from The begin tile for the aqueduct. * @param [out] tile_to The tile till where to show a selection for the aqueduct. * @return The other end of the aqueduct, or otherwise a tile in line with the aqueduct to cause the right error message. */ static TileIndex GetOtherAqueductEnd(TileIndex tile_from, TileIndex *tile_to = NULL) { int z; DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile_from, &z)); /* If the direction isn't right, just return the next tile so the command * complains about the wrong slope instead of the ends not matching up. * Make sure the coordinate is always a valid tile within the map, so we * don't go "off" the map. That would cause the wrong error message. */ if (!IsValidDiagDirection(dir)) return TILE_ADDXY(tile_from, TileX(tile_from) > 2 ? -1 : 1, 0); /* Direction the aqueduct is built to. */ TileIndexDiff offset = TileOffsByDiagDir(ReverseDiagDir(dir)); /* The maximum length of the aqueduct. */ int max_length = min(_settings_game.construction.max_bridge_length, DistanceFromEdgeDir(tile_from, ReverseDiagDir(dir)) - 1); TileIndex endtile = tile_from; for (int length = 0; IsValidTile(endtile) && TileX(endtile) != 0 && TileY(endtile) != 0; length++) { endtile = TILE_ADD(endtile, offset); if (length > max_length) break; if (GetTileMaxZ(endtile) > z) { if (tile_to != NULL) *tile_to = endtile; break; } } return endtile; }
TileIndex Ship::GetOrderStationLocation(StationID station) { if (station == this->last_station_visited) this->last_station_visited = INVALID_STATION; const Station *st = Station::Get(station); if (st->dock_tile != INVALID_TILE) { return TILE_ADD(st->dock_tile, ToTileIndexDiff(GetDockOffset(st->dock_tile))); } else { this->IncrementRealOrderIndex(); return 0; } }
static bool CheckShipLeaveDepot(Ship *v) { if (!v->IsInDepot()) return false; /* We are leaving a depot, but have to go to the exact same one; re-enter */ if (v->current_order.IsType(OT_GOTO_DEPOT) && IsShipDepotTile(v->tile) && GetDepotIndex(v->tile) == v->current_order.GetDestination()) { VehicleEnterDepot(v); return true; } TileIndex tile = v->tile; Axis axis = GetShipDepotAxis(tile); /* Check first (north) side */ if (DiagdirReachesTracks((DiagDirection)axis) & GetTileShipTrackStatus(TILE_ADD(tile, ToTileIndexDiff(_ship_leave_depot_offs[axis])))) { v->direction = ReverseDir(AxisToDirection(axis)); /* Check second (south) side */ } else if (DiagdirReachesTracks((DiagDirection)(axis + 2)) & GetTileShipTrackStatus(TILE_ADD(tile, -2 * ToTileIndexDiff(_ship_leave_depot_offs[axis])))) { v->direction = AxisToDirection(axis); } else { return false; } v->state = AxisToTrackBits(axis); v->vehstatus &= ~VS_HIDDEN; v->cur_speed = 0; v->UpdateViewport(false, true); SetWindowDirty(WC_VEHICLE_DEPOT, v->tile); PlayShipSound(v); VehicleServiceInDepot(v); InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); SetWindowClassesDirty(WC_SHIPS_LIST); return false; }
static Trackdir ChooseShipTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found) { /* handle special case - when next tile is destination tile */ if (tile == v->dest_tile) { /* convert tracks to trackdirs */ TrackdirBits trackdirs = (TrackdirBits)(tracks | ((int)tracks << 8)); /* limit to trackdirs reachable from enterdir */ trackdirs &= DiagdirReachesTrackdirs(enterdir); /* use vehicle's current direction if that's possible, otherwise use first usable one. */ Trackdir veh_dir = v->GetVehicleTrackdir(); return ((trackdirs & TrackdirToTrackdirBits(veh_dir)) != 0) ? veh_dir : (Trackdir)FindFirstBit2x64(trackdirs); } /* move back to the old tile/trackdir (where ship is coming from) */ TileIndex src_tile = TILE_ADD(tile, TileOffsByDiagDir(ReverseDiagDir(enterdir))); Trackdir trackdir = v->GetVehicleTrackdir(); assert(IsValidTrackdir(trackdir)); /* convert origin trackdir to TrackdirBits */ TrackdirBits trackdirs = TrackdirToTrackdirBits(trackdir); /* get available trackdirs on the destination tile */ TrackdirBits dest_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_WATER, 0)); /* create pathfinder instance */ Tpf pf; /* set origin and destination nodes */ pf.SetOrigin(src_tile, trackdirs); pf.SetDestination(v->dest_tile, dest_trackdirs); /* find best path */ path_found = pf.FindPath(v); Trackdir next_trackdir = INVALID_TRACKDIR; // this would mean "path not found" Node *pNode = pf.GetBestNode(); if (pNode != NULL) { /* walk through the path back to the origin */ Node *pPrevNode = NULL; while (pNode->m_parent != NULL) { pPrevNode = pNode; pNode = pNode->m_parent; } /* return trackdir from the best next node (direct child of origin) */ Node& best_next_node = *pPrevNode; assert(best_next_node.GetTile() == tile); next_trackdir = best_next_node.GetTrackdir(); } return next_trackdir; }
/** * Function performing a search around a center tile and going outward, thus in circle. * Although it really is a square search... * Every tile will be tested by means of the callback function proc, * which will determine if yes or no the given tile meets criteria of search. * @param tile to start the search from. Upon completion, it will return the tile matching the search * @param size: number of tiles per side of the desired search area * @param proc: callback testing function pointer. * @param user_data to be passed to the callback function. Depends on the implementation * @return result of the search * @pre proc != NULL * @pre size > 0 */ bool CircularTileSearch(TileIndex *tile, uint size, TestTileOnSearchProc proc, void *user_data) { assert(proc != NULL); assert(size > 0); if (size % 2 == 1) { /* If the length of the side is uneven, the center has to be checked * separately, as the pattern of uneven sides requires to go around the center */ if (proc(*tile, user_data)) return true; /* If tile test is not successful, get one tile up, * ready for a test in first circle around center tile */ *tile = TILE_ADD(*tile, TileOffsByDir(DIR_N)); return CircularTileSearch(tile, size / 2, 1, 1, proc, user_data); } else { return CircularTileSearch(tile, size / 2, 0, 0, proc, user_data); } }
/** * 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(¤t->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; } }
/** * Find the end of a railway station, from the \a tile, in the direction of \a delta. * @param tile Start tile. * @param delta Movement direction. * @param check_type Stop when the custom station type changes. * @param check_axis Stop when the station direction changes. * @return Found end of the railway station. */ static TileIndex FindRailStationEnd(TileIndex tile, TileIndexDiff delta, bool check_type, bool check_axis) { byte orig_type = 0; Axis orig_axis = AXIS_X; StationID sid = GetStationIndex(tile); if (check_type) orig_type = GetCustomStationSpecIndex(tile); if (check_axis) orig_axis = GetRailStationAxis(tile); for (;;) { TileIndex new_tile = TILE_ADD(tile, delta); if (!IsTileType(new_tile, MP_STATION) || GetStationIndex(new_tile) != sid) break; if (!HasStationRail(new_tile)) break; if (check_type && GetCustomStationSpecIndex(new_tile) != orig_type) break; if (check_axis && GetRailStationAxis(new_tile) != orig_axis) break; tile = new_tile; } return tile; }
/** * returns the track to choose on the next tile, or -1 when it's better to * reverse. The tile given is the tile we are about to enter, enterdir is the * direction in which we are entering the tile */ Track OPFShipChooseTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool &path_found) { assert(IsValidDiagDirection(enterdir)); TileIndex tile2 = TILE_ADD(tile, -TileOffsByDiagDir(enterdir)); Track track; /* Let's find out how far it would be if we would reverse first */ TrackBits b = TrackStatusToTrackBits(GetTileTrackStatus(tile2, TRANSPORT_WATER, 0)) & DiagdirReachesTracks(ReverseDiagDir(enterdir)) & v->state; uint distr = UINT_MAX; // distance if we reversed if (b != 0) { distr = FindShipTrack(v, tile2, ReverseDiagDir(enterdir), b, tile, &track); if (distr != UINT_MAX) distr++; // penalty for reversing } /* And if we would not reverse? */ uint dist = FindShipTrack(v, tile, enterdir, tracks, 0, &track); /* Due to the way this pathfinder works we cannot determine whether we're lost or not. */ path_found = true; if (dist <= distr) return track; return INVALID_TRACK; // We could better reverse }