static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance) { /* Find the closest depot */ const Depot *depot; const Depot *best_depot = NULL; /* If we don't have a maximum distance, i.e. distance = 0, * we want to find any depot so the best distance of no * depot must be more than any correct distance. On the * other hand if we have set a maximum distance, any depot * further away than max_distance can safely be ignored. */ uint best_dist = max_distance == 0 ? UINT_MAX : max_distance + 1; FOR_ALL_DEPOTS(depot) { TileIndex tile = depot->xy; if (IsShipDepotTile(tile) && IsTileOwner(tile, v->owner)) { uint dist = DistanceManhattan(tile, v->tile); if (dist < best_dist) { best_dist = dist; best_depot = depot; } } } return best_depot; }
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; }
/** Build a ship. * @param tile tile of depot where ship is built * @param flags type of operation * @param p1 ship type being built (engine) * @param p2 unused * @param text unused * @return the cost of this operation or an error */ CommandCost CmdBuildShip(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { UnitID unit_num; if (!IsEngineBuildable(p1, VEH_SHIP, _current_company)) return_cmd_error(STR_ERROR_SHIP_NOT_AVAILABLE); const Engine *e = Engine::Get(p1); CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost()); /* Engines without valid cargo should not be available */ if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR; if (flags & DC_QUERY_COST) return value; /* The ai_new queries the vehicle cost before building the route, * so we must check against cheaters no sooner than now. --pasky */ if (!IsShipDepotTile(tile)) return CMD_ERROR; if (!IsTileOwner(tile, _current_company)) return CMD_ERROR; unit_num = (flags & DC_AUTOREPLACE) ? 0 : GetFreeUnitNumber(VEH_SHIP); if (!Vehicle::CanAllocateItem() || unit_num > _settings_game.vehicle.max_ships) return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME); if (flags & DC_EXEC) { int x; int y; const ShipVehicleInfo *svi = &e->u.ship; Ship *v = new Ship(); v->unitnumber = unit_num; v->owner = _current_company; v->tile = tile; x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2; y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2; v->x_pos = x; v->y_pos = y; v->z_pos = GetSlopeZ(x, y); v->UpdateDeltaXY(v->direction); v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL; v->spritenum = svi->image_index; v->cargo_type = e->GetDefaultCargoType(); v->cargo_cap = svi->capacity; v->value = value.GetCost(); v->last_station_visited = INVALID_STATION; v->max_speed = svi->max_speed; v->engine_type = p1; v->reliability = e->reliability; v->reliability_spd_dec = e->reliability_spd_dec; v->max_age = e->GetLifeLengthInDays(); _new_vehicle_id = v->index; v->state = TRACK_BIT_DEPOT; v->service_interval = Company::Get(_current_company)->settings.vehicle.servint_ships; v->date_of_last_service = _date; v->build_year = _cur_year; v->cur_image = SPR_IMG_QUERY; v->random_bits = VehicleRandomBits(); if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE); v->InvalidateNewGRFCacheOfChain(); v->cargo_cap = GetVehicleCapacity(v); v->InvalidateNewGRFCacheOfChain(); VehicleMove(v, false); InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); InvalidateWindowClassesData(WC_SHIPS_LIST, 0); SetWindowDirty(WC_COMPANY, v->owner); if (IsLocalCompany()) { InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Ship window } Company::Get(_current_company)->num_engines[p1]++; } return value; }