# ship_cmd.cpp

Go to the documentation of this file.

    00001 /* $Id: ship_cmd.cpp 18574 2009-12-20 16:19:47Z rubidium $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "ship.h"
    00014 #include "landscape.h"
    00015 #include "timetable.h"
    00016 #include "command_func.h"
    00017 #include "news_func.h"
    00018 #include "company_func.h"
    00019 #include "pathfinder/npf/npf_func.h"
    00020 #include "depot_base.h"
    00021 #include "station_base.h"
    00022 #include "vehicle_gui.h"
    00023 #include "newgrf_engine.h"
    00024 #include "pathfinder/yapf/yapf.h"
    00025 #include "newgrf_sound.h"
    00026 #include "spritecache.h"
    00027 #include "strings_func.h"
    00028 #include "functions.h"
    00029 #include "window_func.h"
    00030 #include "date_func.h"
    00031 #include "vehicle_func.h"
    00032 #include "sound_func.h"
    00033 #include "autoreplace_gui.h"
    00034 #include "gfx_func.h"
    00035 #include "effectvehicle_func.h"
    00036 #include "ai/ai.hpp"
    00037 #include "pathfinder/opf/opf_ship.h"
    00038 #include "landscape_type.h"
    00039
    00040 #include "table/strings.h"
    00041 #include "table/sprites.h"
    00042
    00043 static const uint16 _ship_sprites[] = {0x0E5D, 0x0E55, 0x0E65, 0x0E6D};
    00044
    00045 static inline TrackBits GetTileShipTrackStatus(TileIndex tile)
    00046 {
    00047   return TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0));
    00048 }
    00049
    00050 static SpriteID GetShipIcon(EngineID engine)
    00051 {
    00052   const Engine *e = Engine::Get(engine);
    00053   uint8 spritenum = e->u.ship.image_index;
    00054
    00055   if (is_custom_sprite(spritenum)) {
    00056     SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W);
    00057     if (sprite != 0) return sprite;
    00058
    00059     spritenum = e->original_image_index;
    00060   }
    00061
    00062   return DIR_W + _ship_sprites[spritenum];
    00063 }
    00064
    00065 void DrawShipEngine(int left, int right, int preferred_x, int y, EngineID engine, SpriteID pal)
    00066 {
    00067   SpriteID sprite = GetShipIcon(engine);
    00068   const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL);
    00069   preferred_x = Clamp(preferred_x, left - real_sprite->x_offs, right - real_sprite->width - real_sprite->x_offs);
    00070   DrawSprite(sprite, pal, preferred_x, y);
    00071 }
    00072
    00078 void GetShipSpriteSize(EngineID engine, uint &width, uint &height)
    00079 {
    00080   const Sprite *spr = GetSprite(GetShipIcon(engine), ST_NORMAL);
    00081
    00082   width  = spr->width;
    00083   height = spr->height;
    00084 }
    00085
    00086 SpriteID Ship::GetImage(Direction direction) const
    00087 {
    00088   uint8 spritenum = this->spritenum;
    00089
    00090   if (is_custom_sprite(spritenum)) {
    00091     SpriteID sprite = GetCustomVehicleSprite(this, direction);
    00092     if (sprite != 0) return sprite;
    00093
    00094     spritenum = Engine::Get(this->engine_type)->original_image_index;
    00095   }
    00096
    00097   return _ship_sprites[spritenum] + direction;
    00098 }
    00099
    00100 static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance)
    00101 {
    00102   /* Find the closest depot */
    00103   const Depot *depot;
    00104   const Depot *best_depot = NULL;
    00105   /* If we don't have a maximum distance, i.e. distance = 0,
    00106    * we want to find any depot so the best distance of no
    00107    * depot must be more than any correct distance. On the
    00108    * other hand if we have set a maximum distance, any depot
    00109    * further away than max_distance can safely be ignored. */
    00110   uint best_dist = max_distance == 0 ? UINT_MAX : max_distance + 1;
    00111
    00112   FOR_ALL_DEPOTS(depot) {
    00113     TileIndex tile = depot->xy;
    00114     if (IsShipDepotTile(tile) && IsTileOwner(tile, v->owner)) {
    00115       uint dist = DistanceManhattan(tile, v->tile);
    00116       if (dist < best_dist) {
    00117         best_dist = dist;
    00118         best_depot = depot;
    00119       }
    00120     }
    00121   }
    00122
    00123   return best_depot;
    00124 }
    00125
    00126 static void CheckIfShipNeedsService(Vehicle *v)
    00127 {
    00128   if (Company::Get(v->owner)->settings.vehicle.servint_ships == 0 || !v->NeedsAutomaticServicing()) return;
    00129   if (v->IsInDepot()) {
    00130     VehicleServiceInDepot(v);
    00131     return;
    00132   }
    00133
    00134   uint max_distance;
    00135   switch (_settings_game.pf.pathfinder_for_ships) {
    00136     case VPF_OPF:  max_distance = 12; break;
    00137     case VPF_NPF:  max_distance = _settings_game.pf.npf.maximum_go_to_depot_penalty  / NPF_TILE_LENGTH;  break;
    00138     case VPF_YAPF: max_distance = _settings_game.pf.yapf.maximum_go_to_depot_penalty / YAPF_TILE_LENGTH; break;
    00139     default: NOT_REACHED();
    00140   }
    00141
    00142   const Depot *depot = FindClosestShipDepot(v, max_distance);
    00143
    00144   if (depot == NULL) {
    00145     if (v->current_order.IsType(OT_GOTO_DEPOT)) {
    00146       v->current_order.MakeDummy();
    00147       SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    00148     }
    00149     return;
    00150   }
    00151
    00152   v->current_order.MakeGoToDepot(depot->index, ODTFB_SERVICE);
    00153   v->dest_tile = depot->xy;
    00154   SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    00155 }
    00156
    00157 Money Ship::GetRunningCost() const
    00158 {
    00159   const Engine *e = Engine::Get(this->engine_type);
    00160   uint cost_factor = GetVehicleProperty(this, PROP_SHIP_RUNNING_COST_FACTOR, e->u.ship.running_cost);
    00161   return GetPrice(PR_RUNNING_SHIP, cost_factor, e->grffile);
    00162 }
    00163
    00164 void Ship::OnNewDay()
    00165 {
    00166   if ((++this->day_counter & 7) == 0)
    00167     DecreaseVehicleValue(this);
    00168
    00169   CheckVehicleBreakdown(this);
    00170   AgeVehicle(this);
    00171   CheckIfShipNeedsService(this);
    00172
    00173   CheckOrders(this);
    00174
    00175   if (this->running_ticks == 0) return;
    00176
    00177   CommandCost cost(EXPENSES_SHIP_RUN, this->GetRunningCost() * this->running_ticks / (DAYS_IN_YEAR * DAY_TICKS));
    00178
    00179   this->profit_this_year -= cost.GetCost();
    00180   this->running_ticks = 0;
    00181
    00182   SubtractMoneyFromCompanyFract(this->owner, cost);
    00183
    00184   SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
    00185   /* we need this for the profit */
    00186   SetWindowClassesDirty(WC_SHIPS_LIST);
    00187 }
    00188
    00189 Trackdir Ship::GetVehicleTrackdir() const
    00190 {
    00191   if (this->vehstatus & VS_CRASHED) return INVALID_TRACKDIR;
    00192
    00193   if (this->IsInDepot()) {
    00194     /* We'll assume the ship is facing outwards */
    00195     return DiagDirToDiagTrackdir(GetShipDepotDirection(this->tile));
    00196   }
    00197
    00198   if (this->state == TRACK_BIT_WORMHOLE) {
    00199     /* ship on aqueduct, so just use his direction and assume a diagonal track */
    00200     return DiagDirToDiagTrackdir(DirToDiagDir(this->direction));
    00201   }
    00202
    00203   return TrackDirectionToTrackdir(FindFirstTrack(this->state), this->direction);
    00204 }
    00205
    00206 static void HandleBrokenShip(Vehicle *v)
    00207 {
    00208   if (v->breakdown_ctr != 1) {
    00209     v->breakdown_ctr = 1;
    00210     v->cur_speed = 0;
    00211
    00212     if (v->breakdowns_since_last_service != 255)
    00213       v->breakdowns_since_last_service++;
    00214
    00215     v->MarkDirty();
    00216     SetWindowDirty(WC_VEHICLE_VIEW, v->index);
    00217     SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    00218
    00219     if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
    00220       SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
    00221         SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
    00222     }
    00223
    00224     if (!(v->vehstatus & VS_HIDDEN)) {
    00225       EffectVehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
    00226       if (u != NULL) u->animation_state = v->breakdown_delay * 2;
    00227     }
    00228   }
    00229
    00230   if (!(v->tick_counter & 1)) {
    00231     if (!--v->breakdown_delay) {
    00232       v->breakdown_ctr = 0;
    00233       v->MarkDirty();
    00234       SetWindowDirty(WC_VEHICLE_VIEW, v->index);
    00235     }
    00236   }
    00237 }
    00238
    00239 void Ship::MarkDirty()
    00240 {
    00241   this->UpdateViewport(false, false);
    00242 }
    00243
    00244 static void PlayShipSound(const Vehicle *v)
    00245 {
    00246   if (!PlayVehicleSound(v, VSE_START)) {
    00247     SndPlayVehicleFx(ShipVehInfo(v->engine_type)->sfx, v);
    00248   }
    00249 }
    00250
    00251 void Ship::PlayLeaveStationSound() const
    00252 {
    00253   PlayShipSound(this);
    00254 }
    00255
    00256 TileIndex Ship::GetOrderStationLocation(StationID station)
    00257 {
    00258   if (station == this->last_station_visited) this->last_station_visited = INVALID_STATION;
    00259
    00260   const Station *st = Station::Get(station);
    00261   if (st->dock_tile != INVALID_TILE) {
    00262     return TILE_ADD(st->dock_tile, ToTileIndexDiff(GetDockOffset(st->dock_tile)));
    00263   } else {
    00264     this->IncrementOrderIndex();
    00265     return 0;
    00266   }
    00267 }
    00268
    00269 void Ship::UpdateDeltaXY(Direction direction)
    00270 {
    00271 #define MKIT(a, b, c, d) ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF) << 0)
    00272   static const uint32 _delta_xy_table[8] = {
    00273     MKIT( 6,  6,  -3,  -3),
    00274     MKIT( 6, 32,  -3, -16),
    00275     MKIT( 6,  6,  -3,  -3),
    00276     MKIT(32,  6, -16,  -3),
    00277     MKIT( 6,  6,  -3,  -3),
    00278     MKIT( 6, 32,  -3, -16),
    00279     MKIT( 6,  6,  -3,  -3),
    00280     MKIT(32,  6, -16,  -3),
    00281   };
    00282 #undef MKIT
    00283
    00284   uint32 x = _delta_xy_table[direction];
    00285   this->x_offs        = GB(x,  0, 8);
    00286   this->y_offs        = GB(x,  8, 8);
    00287   this->x_extent      = GB(x, 16, 8);
    00288   this->y_extent      = GB(x, 24, 8);
    00289   this->z_extent      = 6;
    00290 }
    00291
    00292 void RecalcShipStuff(Vehicle *v)
    00293 {
    00294   v->UpdateViewport(false, true);
    00295   SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
    00296 }
    00297
    00298 static const TileIndexDiffC _ship_leave_depot_offs[] = {
    00299   {-1,  0},
    00300   { 0, -1}
    00301 };
    00302
    00303 static void CheckShipLeaveDepot(Ship *v)
    00304 {
    00305   if (!v->IsInDepot()) return;
    00306
    00307   TileIndex tile = v->tile;
    00308   Axis axis = GetShipDepotAxis(tile);
    00309
    00310   /* Check first (north) side */
    00311   if (DiagdirReachesTracks((DiagDirection)axis) & GetTileShipTrackStatus(TILE_ADD(tile, ToTileIndexDiff(_ship_leave_depot_offs[axis])))) {
    00312     v->direction = ReverseDir(AxisToDirection(axis));
    00313   /* Check second (south) side */
    00314   } else if (DiagdirReachesTracks((DiagDirection)(axis + 2)) & GetTileShipTrackStatus(TILE_ADD(tile, -2 * ToTileIndexDiff(_ship_leave_depot_offs[axis])))) {
    00315     v->direction = AxisToDirection(axis);
    00316   } else {
    00317     return;
    00318   }
    00319
    00320   v->state = AxisToTrackBits(axis);
    00321   v->vehstatus &= ~VS_HIDDEN;
    00322
    00323   v->cur_speed = 0;
    00324   RecalcShipStuff(v);
    00325
    00326   PlayShipSound(v);
    00327   VehicleServiceInDepot(v);
    00328   InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
    00329   SetWindowClassesDirty(WC_SHIPS_LIST);
    00330 }
    00331
    00332 static bool ShipAccelerate(Vehicle *v)
    00333 {
    00334   uint spd;
    00335   byte t;
    00336
    00337   spd = min(v->cur_speed + 1, GetVehicleProperty(v, PROP_SHIP_SPEED, v->max_speed));
    00338
    00339   /* updates statusbar only if speed have changed to save CPU time */
    00340   if (spd != v->cur_speed) {
    00341     v->cur_speed = spd;
    00342     if (_settings_client.gui.vehicle_speed)
    00343       SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    00344   }
    00345
    00346   /* Decrease somewhat when turning */
    00347   if (!(v->direction & 1)) spd = spd * 3 / 4;
    00348
    00349   if (spd == 0) return false;
    00350   if ((byte)++spd == 0) return true;
    00351
    00352   v->progress = (t = v->progress) - (byte)spd;
    00353
    00354   return (t < v->progress);
    00355 }
    00356
    00357 static void ShipArrivesAt(const Vehicle *v, Station *st)
    00358 {
    00359   /* Check if station was ever visited before */
    00360   if (!(st->had_vehicle_of_type & HVOT_SHIP)) {
    00361     st->had_vehicle_of_type |= HVOT_SHIP;
    00362
    00363     SetDParam(0, st->index);
    00364     AddVehicleNewsItem(
    00365       STR_NEWS_FIRST_SHIP_ARRIVAL,
    00366       (v->owner == _local_company) ? NS_ARRIVAL_COMPANY : NS_ARRIVAL_OTHER,
    00367       v->index,
    00368       st->index
    00369     );
    00370     AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
    00371   }
    00372 }
    00373
    00374
    00378 static Track ChooseShipTrack(const Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks)
    00379 {
    00380   assert(IsValidDiagDirection(enterdir));
    00381
    00382   switch (_settings_game.pf.pathfinder_for_ships) {
    00383     case VPF_OPF: return OPFShipChooseTrack(v, tile, enterdir, tracks);
    00384     case VPF_NPF: return NPFShipChooseTrack(v, tile, enterdir, tracks);
    00385     case VPF_YAPF: return YapfShipChooseTrack(v, tile, enterdir, tracks);
    00386     default: NOT_REACHED();
    00387   }
    00388 }
    00389
    00390 static const Direction _new_vehicle_direction_table[] = {
    00391   DIR_N , DIR_NW, DIR_W , INVALID_DIR,
    00392   DIR_NE, DIR_N , DIR_SW, INVALID_DIR,
    00393   DIR_E , DIR_SE, DIR_S
    00394 };
    00395
    00396 static Direction ShipGetNewDirectionFromTiles(TileIndex new_tile, TileIndex old_tile)
    00397 {
    00398   uint offs = (TileY(new_tile) - TileY(old_tile) + 1) * 4 +
    00399               TileX(new_tile) - TileX(old_tile) + 1;
    00400   assert(offs < 11 && offs != 3 && offs != 7);
    00401   return _new_vehicle_direction_table[offs];
    00402 }
    00403
    00404 static Direction ShipGetNewDirection(Vehicle *v, int x, int y)
    00405 {
    00406   uint offs = (y - v->y_pos + 1) * 4 + (x - v->x_pos + 1);
    00407   assert(offs < 11 && offs != 3 && offs != 7);
    00408   return _new_vehicle_direction_table[offs];
    00409 }
    00410
    00411 static inline TrackBits GetAvailShipTracks(TileIndex tile, DiagDirection dir)
    00412 {
    00413   return GetTileShipTrackStatus(tile) & DiagdirReachesTracks(dir);
    00414 }
    00415
    00416 static const byte _ship_subcoord[4][6][3] = {
    00417   {
    00418     {15, 8, 1},
    00419     { 0, 0, 0},
    00420     { 0, 0, 0},
    00421     {15, 8, 2},
    00422     {15, 7, 0},
    00423     { 0, 0, 0},
    00424   },
    00425   {
    00426     { 0, 0, 0},
    00427     { 8, 0, 3},
    00428     { 7, 0, 2},
    00429     { 0, 0, 0},
    00430     { 8, 0, 4},
    00431     { 0, 0, 0},
    00432   },
    00433   {
    00434     { 0, 8, 5},
    00435     { 0, 0, 0},
    00436     { 0, 7, 6},
    00437     { 0, 0, 0},
    00438     { 0, 0, 0},
    00439     { 0, 8, 4},
    00440   },
    00441   {
    00442     { 0, 0, 0},
    00443     { 8, 15, 7},
    00444     { 0, 0, 0},
    00445     { 8, 15, 6},
    00446     { 0, 0, 0},
    00447     { 7, 15, 0},
    00448   }
    00449 };
    00450
    00451 static void ShipController(Ship *v)
    00452 {
    00453   uint32 r;
    00454   const byte *b;
    00455   Direction dir;
    00456   Track track;
    00457   TrackBits tracks;
    00458
    00459   v->tick_counter++;
    00460   v->current_order_time++;
    00461
    00462   if (v->breakdown_ctr != 0) {
    00463     if (v->breakdown_ctr <= 2) {
    00464       HandleBrokenShip(v);
    00465       return;
    00466     }
    00467     if (!v->current_order.IsType(OT_LOADING)) v->breakdown_ctr--;
    00468   }
    00469
    00470   if (v->vehstatus & VS_STOPPED) return;
    00471
    00472   ProcessOrders(v);
    00473   v->HandleLoading();
    00474
    00475   if (v->current_order.IsType(OT_LOADING)) return;
    00476
    00477   CheckShipLeaveDepot(v);
    00478
    00479   if (!ShipAccelerate(v)) return;
    00480
    00481   GetNewVehiclePosResult gp = GetNewVehiclePos(v);
    00482   if (v->state != TRACK_BIT_WORMHOLE) {
    00483     /* Not on a bridge */
    00484     if (gp.old_tile == gp.new_tile) {
    00485       /* Staying in tile */
    00486       if (v->IsInDepot()) {
    00487         gp.x = v->x_pos;
    00488         gp.y = v->y_pos;
    00489       } else {
    00490         /* Not inside depot */
    00491         r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
    00492         if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;
    00493
    00494         /* A leave station order only needs one tick to get processed, so we can
    00495          * always skip ahead. */
    00496         if (v->current_order.IsType(OT_LEAVESTATION)) {
    00497           v->current_order.Free();
    00498           SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    00499         } else if (v->dest_tile != 0) {
    00500           /* We have a target, let's see if we reached it... */
    00501           if (v->current_order.IsType(OT_GOTO_WAYPOINT) &&
    00502               DistanceManhattan(v->dest_tile, gp.new_tile) <= 3) {
    00503             /* We got within 3 tiles of our target buoy, so let's skip to our
    00504              * next order */
    00505             UpdateVehicleTimetable(v, true);
    00506             v->IncrementOrderIndex();
    00507             v->current_order.MakeDummy();
    00508           } else {
    00509             /* Non-buoy orders really need to reach the tile */
    00510             if (v->dest_tile == gp.new_tile) {
    00511               if (v->current_order.IsType(OT_GOTO_DEPOT)) {
    00512                 if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) {
    00513                   VehicleEnterDepot(v);
    00514                   return;
    00515                 }
    00516               } else if (v->current_order.IsType(OT_GOTO_STATION)) {
    00517                 v->last_station_visited = v->current_order.GetDestination();
    00518
    00519                 /* Process station in the orderlist. */
    00520                 Station *st = Station::Get(v->current_order.GetDestination());
    00521                 if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations
    00522                   ShipArrivesAt(v, st);
    00523                   v->BeginLoading();
    00524                 } else { // leave stations without docks right aways
    00525                   v->current_order.MakeLeaveStation();
    00526                   v->IncrementOrderIndex();
    00527                 }
    00528               }
    00529             }
    00530           }
    00531         }
    00532       }
    00533     } else {
    00534       DiagDirection diagdir;
    00535       /* New tile */
    00536       if (TileX(gp.new_tile) >= MapMaxX() || TileY(gp.new_tile) >= MapMaxY()) {
    00537         goto reverse_direction;
    00538       }
    00539
    00540       dir = ShipGetNewDirectionFromTiles(gp.new_tile, gp.old_tile);
    00541       assert(dir == DIR_NE || dir == DIR_SE || dir == DIR_SW || dir == DIR_NW);
    00542       diagdir = DirToDiagDir(dir);
    00543       tracks = GetAvailShipTracks(gp.new_tile, diagdir);
    00544       if (tracks == TRACK_BIT_NONE) goto reverse_direction;
    00545
    00546       /* Choose a direction, and continue if we find one */
    00547       track = ChooseShipTrack(v, gp.new_tile, diagdir, tracks);
    00548       if (track == INVALID_TRACK) goto reverse_direction;
    00549
    00550       b = _ship_subcoord[diagdir][track];
    00551
    00552       gp.x = (gp.x & ~0xF) | b[0];
    00553       gp.y = (gp.y & ~0xF) | b[1];
    00554
    00555       /* Call the landscape function and tell it that the vehicle entered the tile */
    00556       r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
    00557       if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;
    00558
    00559       if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
    00560         v->tile = gp.new_tile;
    00561         v->state = TrackToTrackBits(track);
    00562       }
    00563
    00564       v->direction = (Direction)b[2];
    00565     }
    00566   } else {
    00567     /* On a bridge */
    00568     if (!IsTileType(gp.new_tile, MP_TUNNELBRIDGE) || !HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
    00569       v->x_pos = gp.x;
    00570       v->y_pos = gp.y;
    00571       VehicleMove(v, !(v->vehstatus & VS_HIDDEN));
    00572       return;
    00573     }
    00574   }
    00575
    00576   /* update image of ship, as well as delta XY */
    00577   dir = ShipGetNewDirection(v, gp.x, gp.y);
    00578   v->x_pos = gp.x;
    00579   v->y_pos = gp.y;
    00580   v->z_pos = GetSlopeZ(gp.x, gp.y);
    00581
    00582 getout:
    00583   v->UpdateViewport(true, true);
    00584   return;
    00585
    00586 reverse_direction:
    00587   dir = ReverseDir(v->direction);
    00588   v->direction = dir;
    00589   goto getout;
    00590 }
    00591
    00592 bool Ship::Tick()
    00593 {
    00594   if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
    00595
    00596   ShipController(this);
    00597
    00598   return true;
    00599 }
    00600
    00609 CommandCost CmdBuildShip(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00610 {
    00611   UnitID unit_num;
    00612
    00613   if (!IsEngineBuildable(p1, VEH_SHIP, _current_company)) return_cmd_error(STR_ERROR_SHIP_NOT_AVAILABLE);
    00614
    00615   const Engine *e = Engine::Get(p1);
    00616   CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost());
    00617
    00618   /* Engines without valid cargo should not be available */
    00619   if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR;
    00620
    00621   if (flags & DC_QUERY_COST) return value;
    00622
    00623   /* The ai_new queries the vehicle cost before building the route,
    00624    * so we must check against cheaters no sooner than now. --pasky */
    00625   if (!IsShipDepotTile(tile)) return CMD_ERROR;
    00626   if (!IsTileOwner(tile, _current_company)) return CMD_ERROR;
    00627
    00628   unit_num = (flags & DC_AUTOREPLACE) ? 0 : GetFreeUnitNumber(VEH_SHIP);
    00629
    00630   if (!Vehicle::CanAllocateItem() || unit_num > _settings_game.vehicle.max_ships)
    00631     return_cmd_error(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME);
    00632
    00633   if (flags & DC_EXEC) {
    00634     int x;
    00635     int y;
    00636
    00637     const ShipVehicleInfo *svi = &e->u.ship;
    00638
    00639     Ship *v = new Ship();
    00640     v->unitnumber = unit_num;
    00641
    00642     v->owner = _current_company;
    00643     v->tile = tile;
    00644     x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2;
    00645     y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2;
    00646     v->x_pos = x;
    00647     v->y_pos = y;
    00648     v->z_pos = GetSlopeZ(x, y);
    00649
    00650     v->running_ticks = 0;
    00651
    00652     v->UpdateDeltaXY(v->direction);
    00653     v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
    00654
    00655     v->spritenum = svi->image_index;
    00656     v->cargo_type = e->GetDefaultCargoType();
    00657     v->cargo_subtype = 0;
    00658     v->cargo_cap = svi->capacity;
    00659     v->value = value.GetCost();
    00660
    00661     v->last_station_visited = INVALID_STATION;
    00662     v->max_speed = svi->max_speed;
    00663     v->engine_type = p1;
    00664
    00665     v->reliability = e->reliability;
    00666     v->reliability_spd_dec = e->reliability_spd_dec;
    00667     v->max_age = e->GetLifeLengthInDays();
    00668     _new_vehicle_id = v->index;
    00669
    00670     v->name = NULL;
    00671     v->state = TRACK_BIT_DEPOT;
    00672
    00673     v->service_interval = Company::Get(_current_company)->settings.vehicle.servint_ships;
    00674     v->date_of_last_service = _date;
    00675     v->build_year = _cur_year;
    00676     v->cur_image = SPR_IMG_QUERY;
    00677     v->random_bits = VehicleRandomBits();
    00678
    00679     v->vehicle_flags = 0;
    00680     if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
    00681
    00682     v->InvalidateNewGRFCacheOfChain();
    00683
    00684     v->cargo_cap = GetVehicleCapacity(v);
    00685
    00686     v->InvalidateNewGRFCacheOfChain();
    00687
    00688     VehicleMove(v, false);
    00689
    00690     InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
    00691     InvalidateWindowClassesData(WC_SHIPS_LIST, 0);
    00692     SetWindowDirty(WC_COMPANY, v->owner);
    00693     if (IsLocalCompany())
    00694       InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Ship window
    00695
    00696     Company::Get(_current_company)->num_engines[p1]++;
    00697   }
    00698
    00699   return value;
    00700 }
    00701
    00710 CommandCost CmdSellShip(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00711 {
    00712   Ship *v = Ship::GetIfValid(p1);
    00713   if (v == NULL || !CheckOwnership(v->owner)) return CMD_ERROR;
    00714
    00715   if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_CAN_T_SELL_DESTROYED_VEHICLE);
    00716
    00717   if (!v->IsStoppedInDepot()) {
    00718     return_cmd_error(STR_ERROR_SHIP_MUST_BE_STOPPED_IN_DEPOT);
    00719   }
    00720
    00721   CommandCost ret(EXPENSES_NEW_VEHICLES, -v->value);
    00722
    00723   if (flags & DC_EXEC) {
    00724     delete v;
    00725   }
    00726
    00727   return ret;
    00728 }
    00729
    00730 bool Ship::FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse)
    00731 {
    00732   const Depot *depot = FindClosestShipDepot(this, 0);
    00733
    00734   if (depot == NULL) return false;
    00735
    00736   if (location    != NULL) *location    = depot->xy;
    00737   if (destination != NULL) *destination = depot->index;
    00738
    00739   return true;
    00740 }
    00741
    00752 CommandCost CmdSendShipToDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00753 {
    00754   if (p2 & DEPOT_MASS_SEND) {
    00755     /* Mass goto depot requested */
    00756     if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR;
    00757     return SendAllVehiclesToDepot(VEH_SHIP, flags, p2 & DEPOT_SERVICE, _current_company, (p2 & VLW_MASK), p1);
    00758   }
    00759
    00760   Ship *v = Ship::GetIfValid(p1);
    00761   if (v == NULL) return CMD_ERROR;
    00762
    00763   return v->SendToDepot(flags, (DepotCommand)(p2 & DEPOT_COMMAND_MASK));
    00764 }
    00765
    00766
    00778 CommandCost CmdRefitShip(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00779 {
    00780   CargoID new_cid = GB(p2, 0, 8); // gets the cargo number
    00781   byte new_subtype = GB(p2, 8, 8);
    00782
    00783   Ship *v = Ship::GetIfValid(p1);
    00784
    00785   if (v == NULL || !CheckOwnership(v->owner)) return CMD_ERROR;
    00786   if (!v->IsStoppedInDepot()) return_cmd_error(STR_ERROR_SHIP_MUST_BE_STOPPED_IN_DEPOT);
    00787   if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_ERROR_CAN_T_REFIT_DESTROYED_VEHICLE);
    00788
    00789   /* Check cargo */
    00790   if (new_cid >= NUM_CARGO) return CMD_ERROR;
    00791
    00792   CommandCost cost = RefitVehicle(v, true, new_cid, new_subtype, flags);
    00793
    00794   if (flags & DC_EXEC) {
    00795     v->colourmap = PAL_NONE; // invalidate vehicle colour map
    00796     SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
    00797     SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
    00798     InvalidateWindowClassesData(WC_SHIPS_LIST, 0);
    00799   }
    00800   v->InvalidateNewGRFCacheOfChain(); // always invalidate; querycost might have filled it
    00801
    00802   return cost;
    00803
    00804 }