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; }
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 void ShipController(Ship *v) { uint32 r; const byte *b; Direction dir; Track track; TrackBits tracks; v->tick_counter++; v->current_order_time++; if (v->HandleBreakdown()) return; if (v->vehstatus & VS_STOPPED) return; ProcessOrders(v); v->HandleLoading(); if (v->current_order.IsType(OT_LOADING)) return; if (CheckShipLeaveDepot(v)) return; v->ShowVisualEffect(); if (!ShipAccelerate(v)) return; GetNewVehiclePosResult gp = GetNewVehiclePos(v); if (v->state != TRACK_BIT_WORMHOLE) { /* Not on a bridge */ if (gp.old_tile == gp.new_tile) { /* Staying in tile */ if (v->IsInDepot()) { gp.x = v->x_pos; gp.y = v->y_pos; } else { /* Not inside depot */ r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y); if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction; /* A leave station order only needs one tick to get processed, so we can * always skip ahead. */ if (v->current_order.IsType(OT_LEAVESTATION)) { v->current_order.Free(); SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP); /* Test if continuing forward would lead to a dead-end, moving into the dock. */ DiagDirection exitdir = VehicleExitDir(v->direction, v->state); TileIndex tile = TileAddByDiagDir(v->tile, exitdir); if (TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0, exitdir)) == TRACK_BIT_NONE) goto reverse_direction; } else if (v->dest_tile != 0) { /* We have a target, let's see if we reached it... */ if (v->current_order.IsType(OT_GOTO_WAYPOINT) && DistanceManhattan(v->dest_tile, gp.new_tile) <= 3) { /* We got within 3 tiles of our target buoy, so let's skip to our * next order */ UpdateVehicleTimetable(v, true); v->IncrementRealOrderIndex(); v->current_order.MakeDummy(); } else { /* Non-buoy orders really need to reach the tile */ if (v->dest_tile == gp.new_tile) { if (v->current_order.IsType(OT_GOTO_DEPOT)) { if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) { VehicleEnterDepot(v); return; } } else if (v->current_order.IsType(OT_GOTO_STATION)) { v->last_station_visited = v->current_order.GetDestination(); /* Process station in the orderlist. */ Station *st = Station::Get(v->current_order.GetDestination()); if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations ShipArrivesAt(v, st); v->BeginLoading(); } else { // leave stations without docks right aways v->current_order.MakeLeaveStation(); v->IncrementRealOrderIndex(); } } } } } } } else { /* New tile */ if (!IsValidTile(gp.new_tile)) goto reverse_direction; DiagDirection diagdir = DiagdirBetweenTiles(gp.old_tile, gp.new_tile); assert(diagdir != INVALID_DIAGDIR); tracks = GetAvailShipTracks(gp.new_tile, diagdir); if (tracks == TRACK_BIT_NONE) goto reverse_direction; /* Choose a direction, and continue if we find one */ track = ChooseShipTrack(v, gp.new_tile, diagdir, tracks); if (track == INVALID_TRACK) goto reverse_direction; b = _ship_subcoord[diagdir][track]; gp.x = (gp.x & ~0xF) | b[0]; gp.y = (gp.y & ~0xF) | b[1]; /* Call the landscape function and tell it that the vehicle entered the tile */ r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y); if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction; if (!HasBit(r, VETS_ENTERED_WORMHOLE)) { v->tile = gp.new_tile; v->state = TrackToTrackBits(track); /* Update ship cache when the water class changes. Aqueducts are always canals. */ WaterClass old_wc = GetEffectiveWaterClass(gp.old_tile); WaterClass new_wc = GetEffectiveWaterClass(gp.new_tile); if (old_wc != new_wc) v->UpdateCache(); } v->direction = (Direction)b[2]; } } else { /* On a bridge */ if (!IsTileType(gp.new_tile, MP_TUNNELBRIDGE) || !HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) { v->x_pos = gp.x; v->y_pos = gp.y; v->UpdatePosition(); if ((v->vehstatus & VS_HIDDEN) == 0) v->Vehicle::UpdateViewport(true); return; } } /* update image of ship, as well as delta XY */ v->x_pos = gp.x; v->y_pos = gp.y; v->z_pos = GetSlopePixelZ(gp.x, gp.y); getout: v->UpdatePosition(); v->UpdateViewport(true, true); return; reverse_direction: dir = ReverseDir(v->direction); v->direction = dir; goto getout; }
static void ShipController(Ship *v) { uint32 r; const byte *b; Direction dir; Track track; TrackBits tracks; v->tick_counter++; v->current_order_time++; if (v->breakdown_ctr != 0) { if (v->breakdown_ctr <= 2) { HandleBrokenShip(v); return; } if (!v->current_order.IsType(OT_LOADING)) v->breakdown_ctr--; } if (v->vehstatus & VS_STOPPED) return; ProcessOrders(v); v->HandleLoading(); if (v->current_order.IsType(OT_LOADING)) return; CheckShipLeaveDepot(v); if (!ShipAccelerate(v)) return; GetNewVehiclePosResult gp = GetNewVehiclePos(v); if (v->state != TRACK_BIT_WORMHOLE) { /* Not on a bridge */ if (gp.old_tile == gp.new_tile) { /* Staying in tile */ if (v->IsInDepot()) { gp.x = v->x_pos; gp.y = v->y_pos; } else { /* Not inside depot */ r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y); if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction; /* A leave station order only needs one tick to get processed, so we can * always skip ahead. */ if (v->current_order.IsType(OT_LEAVESTATION)) { v->current_order.Free(); SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH); } else if (v->dest_tile != 0) { /* We have a target, let's see if we reached it... */ if (v->current_order.IsType(OT_GOTO_WAYPOINT) && DistanceManhattan(v->dest_tile, gp.new_tile) <= 3) { /* We got within 3 tiles of our target buoy, so let's skip to our * next order */ UpdateVehicleTimetable(v, true); v->IncrementOrderIndex(); v->current_order.MakeDummy(); } else { /* Non-buoy orders really need to reach the tile */ if (v->dest_tile == gp.new_tile) { if (v->current_order.IsType(OT_GOTO_DEPOT)) { if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) { VehicleEnterDepot(v); return; } } else if (v->current_order.IsType(OT_GOTO_STATION)) { v->last_station_visited = v->current_order.GetDestination(); /* Process station in the orderlist. */ Station *st = Station::Get(v->current_order.GetDestination()); if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations ShipArrivesAt(v, st); v->BeginLoading(); } else { // leave stations without docks right aways v->current_order.MakeLeaveStation(); v->IncrementOrderIndex(); } } } } } } } else { DiagDirection diagdir; /* New tile */ if (TileX(gp.new_tile) >= MapMaxX() || TileY(gp.new_tile) >= MapMaxY()) { goto reverse_direction; } dir = ShipGetNewDirectionFromTiles(gp.new_tile, gp.old_tile); assert(dir == DIR_NE || dir == DIR_SE || dir == DIR_SW || dir == DIR_NW); diagdir = DirToDiagDir(dir); tracks = GetAvailShipTracks(gp.new_tile, diagdir); if (tracks == TRACK_BIT_NONE) goto reverse_direction; /* Choose a direction, and continue if we find one */ track = ChooseShipTrack(v, gp.new_tile, diagdir, tracks); if (track == INVALID_TRACK) goto reverse_direction; b = _ship_subcoord[diagdir][track]; gp.x = (gp.x & ~0xF) | b[0]; gp.y = (gp.y & ~0xF) | b[1]; /* Call the landscape function and tell it that the vehicle entered the tile */ r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y); if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction; if (!HasBit(r, VETS_ENTERED_WORMHOLE)) { v->tile = gp.new_tile; v->state = TrackToTrackBits(track); } v->direction = (Direction)b[2]; } } else { /* On a bridge */ if (!IsTileType(gp.new_tile, MP_TUNNELBRIDGE) || !HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) { v->x_pos = gp.x; v->y_pos = gp.y; VehicleMove(v, !(v->vehstatus & VS_HIDDEN)); return; } } /* update image of ship, as well as delta XY */ dir = ShipGetNewDirection(v, gp.x, gp.y); v->x_pos = gp.x; v->y_pos = gp.y; v->z_pos = GetSlopeZ(gp.x, gp.y); getout: v->UpdateViewport(true, true); return; reverse_direction: dir = ReverseDir(v->direction); v->direction = dir; goto getout; }