# ship_cmd.cpp

Go to the documentation of this file.

    00001 /* $Id: ship_cmd.cpp 13708 2008-07-16 10:07:38Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "openttd.h"
    00007 #include "ship.h"
    00008 #include "tile_cmd.h"
    00009 #include "landscape.h"
    00010 #include "timetable.h"
    00011 #include "command_func.h"
    00012 #include "pathfind.h"
    00013 #include "station_map.h"
    00014 #include "station.h"
    00015 #include "news.h"
    00016 #include "engine.h"
    00017 #include "player_func.h"
    00018 #include "player_base.h"
    00019 #include "npf.h"
    00020 #include "depot.h"
    00021 #include "vehicle_gui.h"
    00022 #include "newgrf_engine.h"
    00023 #include "water_map.h"
    00024 #include "yapf/yapf.h"
    00025 #include "debug.h"
    00026 #include "newgrf_callbacks.h"
    00027 #include "newgrf_text.h"
    00028 #include "newgrf_sound.h"
    00029 #include "spritecache.h"
    00030 #include "strings_func.h"
    00031 #include "functions.h"
    00032 #include "window_func.h"
    00033 #include "date_func.h"
    00034 #include "vehicle_func.h"
    00035 #include "sound_func.h"
    00036 #include "variables.h"
    00037 #include "autoreplace_gui.h"
    00038 #include "gfx_func.h"
    00039 #include "settings_type.h"
    00040
    00041 #include "table/strings.h"
    00042
    00043 static const uint16 _ship_sprites[] = {0x0E5D, 0x0E55, 0x0E65, 0x0E6D};
    00044
    00045 static const TrackBits _ship_sometracks[4] = {
    00046   TRACK_BIT_X | TRACK_BIT_LOWER | TRACK_BIT_LEFT,  // 0x19, // DIAGDIR_NE
    00047   TRACK_BIT_Y | TRACK_BIT_UPPER | TRACK_BIT_LEFT,  // 0x16, // DIAGDIR_SE
    00048   TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT, // 0x25, // DIAGDIR_SW
    00049   TRACK_BIT_Y | TRACK_BIT_LOWER | TRACK_BIT_RIGHT, // 0x2A, // DIAGDIR_NW
    00050 };
    00051
    00052 static inline TrackBits GetTileShipTrackStatus(TileIndex tile)
    00053 {
    00054   return TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0));
    00055 }
    00056
    00057 void DrawShipEngine(int x, int y, EngineID engine, SpriteID pal)
    00058 {
    00059   int spritenum = ShipVehInfo(engine)->image_index;
    00060
    00061   if (is_custom_sprite(spritenum)) {
    00062     int sprite = GetCustomVehicleIcon(engine, DIR_W);
    00063
    00064     if (sprite != 0) {
    00065       DrawSprite(sprite, pal, x, y);
    00066       return;
    00067     }
    00068     spritenum = _orig_ship_vehicle_info[engine - SHIP_ENGINES_INDEX].image_index;
    00069   }
    00070   DrawSprite(6 + _ship_sprites[spritenum], pal, x, y);
    00071 }
    00072
    00078 void GetShipSpriteSize(EngineID engine, uint &width, uint &height)
    00079 {
    00080   SpriteID spritenum = ShipVehInfo(engine)->image_index;
    00081   SpriteID custom_sprite = 0;
    00082
    00083   if (is_custom_sprite(spritenum)) {
    00084     custom_sprite = GetCustomVehicleIcon(engine, DIR_W);
    00085     spritenum = _orig_ship_vehicle_info[engine - SHIP_ENGINES_INDEX].image_index;
    00086   }
    00087   if (custom_sprite == 0) {
    00088     spritenum = 6 + _ship_sprites[spritenum];
    00089   } else {
    00090     spritenum = custom_sprite;
    00091   }
    00092
    00093   const Sprite *spr = GetSprite(spritenum);
    00094
    00095   width  = spr->width;
    00096   height = spr->height;
    00097 }
    00098
    00099 int Ship::GetImage(Direction direction) const
    00100 {
    00101   int spritenum = this->spritenum;
    00102
    00103   if (is_custom_sprite(spritenum)) {
    00104     int sprite = GetCustomVehicleSprite(this, direction);
    00105
    00106     if (sprite != 0) return sprite;
    00107     spritenum = _orig_ship_vehicle_info[this->engine_type - SHIP_ENGINES_INDEX].image_index;
    00108   }
    00109   return _ship_sprites[spritenum] + direction;
    00110 }
    00111
    00112 static const Depot* FindClosestShipDepot(const Vehicle* v)
    00113 {
    00114   if (_patches.pathfinder_for_ships == VPF_NPF) { /* NPF is used */
    00115     Trackdir trackdir = GetVehicleTrackdir(v);
    00116     NPFFoundTargetData ftd = NPFRouteToDepotTrialError(v->tile, trackdir, false, TRANSPORT_WATER, 0, v->owner, INVALID_RAILTYPES);
    00117
    00118     if (ftd.best_bird_dist == 0) return GetDepotByTile(ftd.node.tile); /* Found target */
    00119
    00120     return NULL; /* Did not find target */
    00121   }
    00122
    00123   /* OPF or YAPF - find the closest depot */
    00124
    00125   const Depot* depot;
    00126   const Depot* best_depot = NULL;
    00127   uint best_dist = UINT_MAX;
    00128
    00129   FOR_ALL_DEPOTS(depot) {
    00130     TileIndex tile = depot->xy;
    00131     if (IsTileDepotType(tile, TRANSPORT_WATER) && IsTileOwner(tile, v->owner)) {
    00132       uint dist = DistanceManhattan(tile, v->tile);
    00133       if (dist < best_dist) {
    00134         best_dist = dist;
    00135         best_depot = depot;
    00136       }
    00137     }
    00138   }
    00139
    00140   return best_depot;
    00141 }
    00142
    00143 static void CheckIfShipNeedsService(Vehicle *v)
    00144 {
    00145   if (_patches.servint_ships == 0 || !v->NeedsAutomaticServicing()) return;
    00146   if (v->IsInDepot()) {
    00147     VehicleServiceInDepot(v);
    00148     return;
    00149   }
    00150
    00151   const Depot *depot = FindClosestShipDepot(v);
    00152
    00153   if (depot == NULL || DistanceManhattan(v->tile, depot->xy) > 12) {
    00154     if (v->current_order.type == OT_GOTO_DEPOT) {
    00155       v->current_order.type = OT_DUMMY;
    00156       v->current_order.flags = 0;
    00157       InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    00158     }
    00159     return;
    00160   }
    00161
    00162   v->current_order.type = OT_GOTO_DEPOT;
    00163   v->current_order.flags = OFB_NON_STOP;
    00164   v->current_order.dest = depot->index;
    00165   v->dest_tile = depot->xy;
    00166   InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    00167 }
    00168
    00169 void Ship::OnNewDay()
    00170 {
    00171   if ((++this->day_counter & 7) == 0)
    00172     DecreaseVehicleValue(this);
    00173
    00174   CheckVehicleBreakdown(this);
    00175   AgeVehicle(this);
    00176   CheckIfShipNeedsService(this);
    00177
    00178   CheckOrders(this);
    00179
    00180   if (this->running_ticks == 0) return;
    00181
    00182   CommandCost cost(EXPENSES_SHIP_RUN, GetVehicleProperty(this, 0x0F, ShipVehInfo(this->engine_type)->running_cost) * _price.ship_running * this->running_ticks / (364 * DAY_TICKS));
    00183
    00184   this->profit_this_year -= cost.GetCost();
    00185   this->running_ticks = 0;
    00186
    00187   SubtractMoneyFromPlayerFract(this->owner, cost);
    00188
    00189   InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
    00190   /* we need this for the profit */
    00191   InvalidateWindowClasses(WC_SHIPS_LIST);
    00192 }
    00193
    00194 static void HandleBrokenShip(Vehicle *v)
    00195 {
    00196   if (v->breakdown_ctr != 1) {
    00197     v->breakdown_ctr = 1;
    00198     v->cur_speed = 0;
    00199
    00200     if (v->breakdowns_since_last_service != 255)
    00201       v->breakdowns_since_last_service++;
    00202
    00203     InvalidateWindow(WC_VEHICLE_VIEW, v->index);
    00204     InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
    00205
    00206     if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
    00207       SndPlayVehicleFx((_opt.landscape != LT_TOYLAND) ?
    00208         SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
    00209     }
    00210
    00211     if (!(v->vehstatus & VS_HIDDEN)) {
    00212       Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
    00213       if (u != NULL) u->u.special.animation_state = v->breakdown_delay * 2;
    00214     }
    00215   }
    00216
    00217   if (!(v->tick_counter & 1)) {
    00218     if (!--v->breakdown_delay) {
    00219       v->breakdown_ctr = 0;
    00220       InvalidateWindow(WC_VEHICLE_VIEW, v->index);
    00221     }
    00222   }
    00223 }
    00224
    00225 void Ship::MarkDirty()
    00226 {
    00227   this->cur_image = this->GetImage(this->direction);
    00228   MarkSingleVehicleDirty(this);
    00229 }
    00230
    00231 static void PlayShipSound(const Vehicle *v)
    00232 {
    00233   if (!PlayVehicleSound(v, VSE_START)) {
    00234     SndPlayVehicleFx(ShipVehInfo(v->engine_type)->sfx, v);
    00235   }
    00236 }
    00237
    00238 void Ship::PlayLeaveStationSound() const
    00239 {
    00240   PlayShipSound(this);
    00241 }
    00242
    00243 static void ProcessShipOrder(Vehicle *v)
    00244 {
    00245   const Order *order;
    00246
    00247   switch (v->current_order.type) {
    00248     case OT_GOTO_DEPOT:
    00249       if (!(v->current_order.flags & OFB_PART_OF_ORDERS)) return;
    00250       if (v->current_order.flags & OFB_SERVICE_IF_NEEDED &&
    00251           !v->NeedsServicing()) {
    00252         UpdateVehicleTimetable(v, true);
    00253         v->cur_order_index++;
    00254       }
    00255       break;
    00256
    00257     case OT_LOADING:
    00258     case OT_LEAVESTATION:
    00259       return;
    00260
    00261     default: break;
    00262   }
    00263
    00264   if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0;
    00265
    00266   order = GetVehicleOrder(v, v->cur_order_index);
    00267
    00268   if (order == NULL) {
    00269     v->current_order.Free();
    00270     v->dest_tile = 0;
    00271     return;
    00272   }
    00273
    00274   if (order->type  == v->current_order.type &&
    00275       order->flags == v->current_order.flags &&
    00276       order->dest  == v->current_order.dest &&
    00277       (order->type != OT_GOTO_STATION || GetStation(order->dest)->dock_tile != 0))
    00278     return;
    00279
    00280   v->current_order = *order;
    00281
    00282   if (order->type == OT_GOTO_STATION) {
    00283     const Station *st;
    00284
    00285     if (order->dest == v->last_station_visited)
    00286       v->last_station_visited = INVALID_STATION;
    00287
    00288     st = GetStation(order->dest);
    00289     if (st->dock_tile != 0) {
    00290       v->dest_tile = TILE_ADD(st->dock_tile, ToTileIndexDiff(GetDockOffset(st->dock_tile)));
    00291     } else {
    00292       v->cur_order_index++;
    00293     }
    00294   } else if (order->type == OT_GOTO_DEPOT) {
    00295     v->dest_tile = GetDepot(order->dest)->xy;
    00296   } else {
    00297     v->dest_tile = 0;
    00298   }
    00299
    00300   InvalidateVehicleOrder(v);
    00301
    00302   InvalidateWindowClasses(WC_SHIPS_LIST);
    00303 }
    00304
    00305 void Ship::UpdateDeltaXY(Direction direction)
    00306 {
    00307 #define MKIT(a, b, c, d) ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF) << 0)
    00308   static const uint32 _delta_xy_table[8] = {
    00309     MKIT( 6,  6,  -3,  -3),
    00310     MKIT( 6, 32,  -3, -16),
    00311     MKIT( 6,  6,  -3,  -3),
    00312     MKIT(32,  6, -16,  -3),
    00313     MKIT( 6,  6,  -3,  -3),
    00314     MKIT( 6, 32,  -3, -16),
    00315     MKIT( 6,  6,  -3,  -3),
    00316     MKIT(32,  6, -16,  -3),
    00317   };
    00318 #undef MKIT
    00319
    00320   uint32 x = _delta_xy_table[direction];
    00321   this->x_offs        = GB(x,  0, 8);
    00322   this->y_offs        = GB(x,  8, 8);
    00323   this->sprite_width  = GB(x, 16, 8);
    00324   this->sprite_height = GB(x, 24, 8);
    00325   this->z_height      = 6;
    00326 }
    00327
    00328 void RecalcShipStuff(Vehicle *v)
    00329 {
    00330   v->UpdateDeltaXY(v->direction);
    00331   v->cur_image = v->GetImage(v->direction);
    00332   v->MarkDirty();
    00333   InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
    00334 }
    00335
    00336 static const TileIndexDiffC _ship_leave_depot_offs[] = {
    00337   {-1,  0},
    00338   { 0, -1}
    00339 };
    00340
    00341 static void CheckShipLeaveDepot(Vehicle *v)
    00342 {
    00343   TileIndex tile;
    00344   Axis axis;
    00345   uint m;
    00346
    00347   if (!v->IsInDepot()) return;
    00348
    00349   tile = v->tile;
    00350   axis = GetShipDepotAxis(tile);
    00351
    00352   /* Check first side */
    00353   if (_ship_sometracks[axis] & GetTileShipTrackStatus(TILE_ADD(tile, ToTileIndexDiff(_ship_leave_depot_offs[axis])))) {
    00354     m = (axis == AXIS_X) ? 0x101 : 0x207;
    00355   /* Check second side */
    00356   } else if (_ship_sometracks[axis + 2] & GetTileShipTrackStatus(TILE_ADD(tile, -2 * ToTileIndexDiff(_ship_leave_depot_offs[axis])))) {
    00357     m = (axis == AXIS_X) ? 0x105 : 0x203;
    00358   } else {
    00359     return;
    00360   }
    00361   v->direction    = (Direction)GB(m, 0, 8);
    00362   v->u.ship.state = (TrackBits)GB(m, 8, 8);
    00363   v->vehstatus &= ~VS_HIDDEN;
    00364
    00365   v->cur_speed = 0;
    00366   RecalcShipStuff(v);
    00367
    00368   PlayShipSound(v);
    00369   VehicleServiceInDepot(v);
    00370   InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
    00371   InvalidateWindowClasses(WC_SHIPS_LIST);
    00372 }
    00373
    00374 static bool ShipAccelerate(Vehicle *v)
    00375 {
    00376   uint spd;
    00377   byte t;
    00378
    00379   spd = min(v->cur_speed + 1, GetVehicleProperty(v, 0x0B, v->max_speed));
    00380
    00381   /*updates statusbar only if speed have changed to save CPU time */
    00382   if (spd != v->cur_speed) {
    00383     v->cur_speed = spd;
    00384     if (_patches.vehicle_speed)
    00385       InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    00386   }
    00387
    00388   /* Decrease somewhat when turning */
    00389   if (!(v->direction & 1)) spd = spd * 3 / 4;
    00390
    00391   if (spd == 0) return false;
    00392   if ((byte)++spd == 0) return true;
    00393
    00394   v->progress = (t = v->progress) - (byte)spd;
    00395
    00396   return (t < v->progress);
    00397 }
    00398
    00399 static CommandCost EstimateShipCost(EngineID engine_type)
    00400 {
    00401   return CommandCost(EXPENSES_NEW_VEHICLES, GetEngineProperty(engine_type, 0x0A, ShipVehInfo(engine_type)->base_cost) * (_price.ship_base >> 3) >> 5);
    00402 }
    00403
    00404 static void ShipArrivesAt(const Vehicle* v, Station* st)
    00405 {
    00406   /* Check if station was ever visited before */
    00407   if (!(st->had_vehicle_of_type & HVOT_SHIP)) {
    00408     uint32 flags;
    00409
    00410     st->had_vehicle_of_type |= HVOT_SHIP;
    00411
    00412     SetDParam(0, st->index);
    00413     flags = (v->owner == _local_player) ? NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_PLAYER, 0) : NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_VEHICLE, NT_ARRIVAL_OTHER, 0);
    00414     AddNewsItem(
    00415       STR_9833_CITIZENS_CELEBRATE_FIRST,
    00416       flags,
    00417       v->index,
    00418       0);
    00419   }
    00420 }
    00421
    00422 struct PathFindShip {
    00423   TileIndex skiptile;
    00424   TileIndex dest_coords;
    00425   uint best_bird_dist;
    00426   uint best_length;
    00427 };
    00428
    00429 static bool ShipTrackFollower(TileIndex tile, PathFindShip *pfs, int track, uint length)
    00430 {
    00431   /* Found dest? */
    00432   if (tile == pfs->dest_coords) {
    00433     pfs->best_bird_dist = 0;
    00434
    00435     pfs->best_length = minu(pfs->best_length, length);
    00436     return true;
    00437   }
    00438
    00439   /* Skip this tile in the calculation */
    00440   if (tile != pfs->skiptile) {
    00441     pfs->best_bird_dist = minu(pfs->best_bird_dist, DistanceMaxPlusManhattan(pfs->dest_coords, tile));
    00442   }
    00443
    00444   return false;
    00445 }
    00446
    00447 static const byte _ship_search_directions[6][4] = {
    00448   { 0, 9, 2, 9 },
    00449   { 9, 1, 9, 3 },
    00450   { 9, 0, 3, 9 },
    00451   { 1, 9, 9, 2 },
    00452   { 3, 2, 9, 9 },
    00453   { 9, 9, 1, 0 },
    00454 };
    00455
    00456 static const byte _pick_shiptrack_table[6] = {1, 3, 2, 2, 0, 0};
    00457
    00458 static uint FindShipTrack(Vehicle *v, TileIndex tile, DiagDirection dir, TrackBits bits, TileIndex skiptile, Track *track)
    00459 {
    00460   PathFindShip pfs;
    00461   Track i, best_track;
    00462   uint best_bird_dist = 0;
    00463   uint best_length    = 0;
    00464   uint r;
    00465   byte ship_dir = v->direction & 3;
    00466
    00467   pfs.dest_coords = v->dest_tile;
    00468   pfs.skiptile = skiptile;
    00469
    00470   best_track = INVALID_TRACK;
    00471
    00472   do {
    00473     i = RemoveFirstTrack(&bits);
    00474
    00475     pfs.best_bird_dist = (uint)-1;
    00476     pfs.best_length = (uint)-1;
    00477
    00478     FollowTrack(tile, 0x1800 | TRANSPORT_WATER, 0, (DiagDirection)_ship_search_directions[i][dir], (TPFEnumProc*)ShipTrackFollower, NULL, &pfs);
    00479
    00480     if (best_track != INVALID_TRACK) {
    00481       if (pfs.best_bird_dist != 0) {
    00482         /* neither reached the destination, pick the one with the smallest bird dist */
    00483         if (pfs.best_bird_dist > best_bird_dist) goto bad;
    00484         if (pfs.best_bird_dist < best_bird_dist) goto good;
    00485       } else {
    00486         if (pfs.best_length > best_length) goto bad;
    00487         if (pfs.best_length < best_length) goto good;
    00488       }
    00489
    00490       /* if we reach this position, there's two paths of equal value so far.
    00491        * pick one randomly. */
    00492       r = GB(Random(), 0, 8);
    00493       if (_pick_shiptrack_table[i] == ship_dir) r += 80;
    00494       if (_pick_shiptrack_table[best_track] == ship_dir) r -= 80;
    00495       if (r <= 127) goto bad;
    00496     }
    00497 good:;
    00498     best_track = i;
    00499     best_bird_dist = pfs.best_bird_dist;
    00500     best_length = pfs.best_length;
    00501 bad:;
    00502
    00503   } while (bits != 0);
    00504
    00505   *track = best_track;
    00506   return best_bird_dist;
    00507 }
    00508
    00509 static inline NPFFoundTargetData PerfNPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, bool ignore_start_tile, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailTypes railtypes)
    00510 {
    00511
    00512   void* perf = NpfBeginInterval();
    00513   NPFFoundTargetData ret = NPFRouteToStationOrTile(tile, trackdir, ignore_start_tile, target, type, 0, owner, railtypes);
    00514   int t = NpfEndInterval(perf);
    00515   DEBUG(yapf, 4, "[NPFW] %d us - %d rounds - %d open - %d closed -- ", t, 0, _aystar_stats_open_size, _aystar_stats_closed_size);
    00516   return ret;
    00517 }
    00518
    00522 static Track ChooseShipTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks)
    00523 {
    00524   assert(IsValidDiagDirection(enterdir));
    00525
    00526   switch (_patches.pathfinder_for_ships) {
    00527     case VPF_YAPF: { /* YAPF */
    00528       Trackdir trackdir = YapfChooseShipTrack(v, tile, enterdir, tracks);
    00529       if (trackdir != INVALID_TRACKDIR) return TrackdirToTrack(trackdir);
    00530     } break;
    00531
    00532     case VPF_NPF: { /* NPF */
    00533       NPFFindStationOrTileData fstd;
    00534       Trackdir trackdir = GetVehicleTrackdir(v);
    00535       assert(trackdir != INVALID_TRACKDIR); // Check that we are not in a depot
    00536
    00537       NPFFillWithOrderData(&fstd, v);
    00538
    00539       NPFFoundTargetData ftd = PerfNPFRouteToStationOrTile(tile - TileOffsByDiagDir(enterdir), trackdir, true, &fstd, TRANSPORT_WATER, v->owner, INVALID_RAILTYPES);
    00540
    00541       /* If ftd.best_bird_dist is 0, we found our target and ftd.best_trackdir contains
    00542        * the direction we need to take to get there, if ftd.best_bird_dist is not 0,
    00543        * we did not find our target, but ftd.best_trackdir contains the direction leading
    00544        * to the tile closest to our target. */
    00545       if (ftd.best_trackdir != 0xff) return TrackdirToTrack(ftd.best_trackdir); /* TODO: Wrapper function? */
    00546     } break;
    00547
    00548     default:
    00549     case VPF_OPF: { /* OPF */
    00550       TileIndex tile2 = TILE_ADD(tile, -TileOffsByDiagDir(enterdir));
    00551       Track track;
    00552
    00553       /* Let's find out how far it would be if we would reverse first */
    00554       TrackBits b = GetTileShipTrackStatus(tile2) & _ship_sometracks[ReverseDiagDir(enterdir)] & v->u.ship.state;
    00555
    00556       uint distr = UINT_MAX; // distance if we reversed
    00557       if (b != 0) {
    00558         distr = FindShipTrack(v, tile2, ReverseDiagDir(enterdir), b, tile, &track);
    00559         if (distr != UINT_MAX) distr++; // penalty for reversing
    00560       }
    00561
    00562       /* And if we would not reverse? */
    00563       uint dist = FindShipTrack(v, tile, enterdir, tracks, 0, &track);
    00564
    00565       if (dist <= distr) return track;
    00566     } break;
    00567   }
    00568
    00569   return INVALID_TRACK; /* We could better reverse */
    00570 }
    00571
    00572 static const Direction _new_vehicle_direction_table[] = {
    00573   DIR_N , DIR_NW, DIR_W , INVALID_DIR,
    00574   DIR_NE, DIR_N , DIR_SW, INVALID_DIR,
    00575   DIR_E , DIR_SE, DIR_S
    00576 };
    00577
    00578 static Direction ShipGetNewDirectionFromTiles(TileIndex new_tile, TileIndex old_tile)
    00579 {
    00580   uint offs = (TileY(new_tile) - TileY(old_tile) + 1) * 4 +
    00581               TileX(new_tile) - TileX(old_tile) + 1;
    00582   assert(offs < 11 && offs != 3 && offs != 7);
    00583   return _new_vehicle_direction_table[offs];
    00584 }
    00585
    00586 static Direction ShipGetNewDirection(Vehicle *v, int x, int y)
    00587 {
    00588   uint offs = (y - v->y_pos + 1) * 4 + (x - v->x_pos + 1);
    00589   assert(offs < 11 && offs != 3 && offs != 7);
    00590   return _new_vehicle_direction_table[offs];
    00591 }
    00592
    00593 static inline TrackBits GetAvailShipTracks(TileIndex tile, int dir)
    00594 {
    00595   return GetTileShipTrackStatus(tile) & _ship_sometracks[dir];
    00596 }
    00597
    00598 static const byte _ship_subcoord[4][6][3] = {
    00599   {
    00600     {15, 8, 1},
    00601     { 0, 0, 0},
    00602     { 0, 0, 0},
    00603     {15, 8, 2},
    00604     {15, 7, 0},
    00605     { 0, 0, 0},
    00606   },
    00607   {
    00608     { 0, 0, 0},
    00609     { 8, 0, 3},
    00610     { 7, 0, 2},
    00611     { 0, 0, 0},
    00612     { 8, 0, 4},
    00613     { 0, 0, 0},
    00614   },
    00615   {
    00616     { 0, 8, 5},
    00617     { 0, 0, 0},
    00618     { 0, 7, 6},
    00619     { 0, 0, 0},
    00620     { 0, 0, 0},
    00621     { 0, 8, 4},
    00622   },
    00623   {
    00624     { 0, 0, 0},
    00625     { 8, 15, 7},
    00626     { 0, 0, 0},
    00627     { 8, 15, 6},
    00628     { 0, 0, 0},
    00629     { 7, 15, 0},
    00630   }
    00631 };
    00632
    00633 static void ShipController(Vehicle *v)
    00634 {
    00635   uint32 r;
    00636   const byte *b;
    00637   Direction dir;
    00638   Track track;
    00639   TrackBits tracks;
    00640
    00641   v->tick_counter++;
    00642   v->current_order_time++;
    00643
    00644   if (v->breakdown_ctr != 0) {
    00645     if (v->breakdown_ctr <= 2) {
    00646       HandleBrokenShip(v);
    00647       return;
    00648     }
    00649     if (v->current_order.type != OT_LOADING) v->breakdown_ctr--;
    00650   }
    00651
    00652   if (v->vehstatus & VS_STOPPED) return;
    00653
    00654   ProcessShipOrder(v);
    00655   v->HandleLoading();
    00656
    00657   if (v->current_order.type == OT_LOADING) return;
    00658
    00659   CheckShipLeaveDepot(v);
    00660
    00661   if (!ShipAccelerate(v)) return;
    00662
    00663   BeginVehicleMove(v);
    00664
    00665   GetNewVehiclePosResult gp = GetNewVehiclePos(v);
    00666   if (gp.old_tile == gp.new_tile) {
    00667     /* Staying in tile */
    00668     if (v->IsInDepot()) {
    00669       gp.x = v->x_pos;
    00670       gp.y = v->y_pos;
    00671     } else {
    00672       /* Not inside depot */
    00673       r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
    00674       if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;
    00675
    00676       /* A leave station order only needs one tick to get processed, so we can
    00677        * always skip ahead. */
    00678       if (v->current_order.type == OT_LEAVESTATION) {
    00679         v->current_order.Free();
    00680         InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    00681       } else if (v->dest_tile != 0) {
    00682         /* We have a target, let's see if we reached it... */
    00683         if (v->current_order.type == OT_GOTO_STATION &&
    00684             IsBuoyTile(v->dest_tile) &&
    00685             DistanceManhattan(v->dest_tile, gp.new_tile) <= 3) {
    00686           /* We got within 3 tiles of our target buoy, so let's skip to our
    00687            * next order */
    00688           UpdateVehicleTimetable(v, true);
    00689           v->cur_order_index++;
    00690           v->current_order.type = OT_DUMMY;
    00691           InvalidateVehicleOrder(v);
    00692         } else {
    00693           /* Non-buoy orders really need to reach the tile */
    00694           if (v->dest_tile == gp.new_tile) {
    00695             if (v->current_order.type == OT_GOTO_DEPOT) {
    00696               if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) {
    00697                 VehicleEnterDepot(v);
    00698                 return;
    00699               }
    00700             } else if (v->current_order.type == OT_GOTO_STATION) {
    00701               Station *st;
    00702
    00703               v->last_station_visited = v->current_order.dest;
    00704
    00705               /* Process station in the orderlist. */
    00706               st = GetStation(v->current_order.dest);
    00707               if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations
    00708                 ShipArrivesAt(v, st);
    00709                 v->BeginLoading();
    00710               } else { // leave stations without docks right aways
    00711                 v->current_order.type = OT_LEAVESTATION;
    00712                 v->cur_order_index++;
    00713                 InvalidateVehicleOrder(v);
    00714               }
    00715             }
    00716           }
    00717         }
    00718       }
    00719     }
    00720   } else {
    00721     DiagDirection diagdir;
    00722     /* New tile */
    00723     if (TileX(gp.new_tile) >= MapMaxX() || TileY(gp.new_tile) >= MapMaxY()) {
    00724       goto reverse_direction;
    00725     }
    00726
    00727     dir = ShipGetNewDirectionFromTiles(gp.new_tile, gp.old_tile);
    00728     assert(dir == DIR_NE || dir == DIR_SE || dir == DIR_SW || dir == DIR_NW);
    00729     diagdir = DirToDiagDir(dir);
    00730     tracks = GetAvailShipTracks(gp.new_tile, diagdir);
    00731     if (tracks == TRACK_BIT_NONE) goto reverse_direction;
    00732
    00733     /* Choose a direction, and continue if we find one */
    00734     track = ChooseShipTrack(v, gp.new_tile, diagdir, tracks);
    00735     if (track == INVALID_TRACK) goto reverse_direction;
    00736
    00737     b = _ship_subcoord[diagdir][track];
    00738
    00739     gp.x = (gp.x & ~0xF) | b[0];
    00740     gp.y = (gp.y & ~0xF) | b[1];
    00741
    00742     /* Call the landscape function and tell it that the vehicle entered the tile */
    00743     r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
    00744     if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;
    00745
    00746     if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
    00747       v->tile = gp.new_tile;
    00748       v->u.ship.state = TrackToTrackBits(track);
    00749     }
    00750
    00751     v->direction = (Direction)b[2];
    00752   }
    00753
    00754   /* update image of ship, as well as delta XY */
    00755   dir = ShipGetNewDirection(v, gp.x, gp.y);
    00756   v->x_pos = gp.x;
    00757   v->y_pos = gp.y;
    00758   v->z_pos = GetSlopeZ(gp.x, gp.y);
    00759
    00760 getout:
    00761   v->UpdateDeltaXY(dir);
    00762   v->cur_image = v->GetImage(dir);
    00763   VehiclePositionChanged(v);
    00764   EndVehicleMove(v);
    00765   return;
    00766
    00767 reverse_direction:
    00768   dir = ReverseDir(v->direction);
    00769   v->direction = dir;
    00770   goto getout;
    00771 }
    00772
    00773 static void AgeShipCargo(Vehicle *v)
    00774 {
    00775   if (_age_cargo_skip_counter != 0) return;
    00776   v->cargo.AgeCargo();
    00777 }
    00778
    00779 void Ship::Tick()
    00780 {
    00781   if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
    00782
    00783   AgeShipCargo(this);
    00784   ShipController(this);
    00785 }
    00786
    00787
    00788 void ShipsYearlyLoop()
    00789 {
    00790   Vehicle *v;
    00791
    00792   FOR_ALL_VEHICLES(v) {
    00793     if (v->type == VEH_SHIP) {
    00794       v->profit_last_year = v->profit_this_year;
    00795       v->profit_this_year = 0;
    00796       InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
    00797     }
    00798   }
    00799 }
    00800
    00807 CommandCost CmdBuildShip(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
    00808 {
    00809   CommandCost value;
    00810   UnitID unit_num;
    00811   Engine *e;
    00812
    00813   if (!IsEngineBuildable(p1, VEH_SHIP, _current_player)) return_cmd_error(STR_SHIP_NOT_AVAILABLE);
    00814
    00815   value = EstimateShipCost(p1);
    00816   if (flags & DC_QUERY_COST) return value;
    00817
    00818   /* The ai_new queries the vehicle cost before building the route,
    00819    * so we must check against cheaters no sooner than now. --pasky */
    00820   if (!IsTileDepotType(tile, TRANSPORT_WATER)) return CMD_ERROR;
    00821   if (!IsTileOwner(tile, _current_player)) return CMD_ERROR;
    00822
    00823   unit_num = (flags & DC_AUTOREPLACE) ? 0 : GetFreeUnitNumber(VEH_SHIP);
    00824
    00825   if (!Vehicle::AllocateList(NULL, 1) || unit_num > _patches.max_ships)
    00826     return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
    00827
    00828   if (flags & DC_EXEC) {
    00829     int x;
    00830     int y;
    00831
    00832     const ShipVehicleInfo *svi = ShipVehInfo(p1);
    00833
    00834     Vehicle *v = new Ship();
    00835     v->unitnumber = unit_num;
    00836
    00837     v->owner = _current_player;
    00838     v->tile = tile;
    00839     x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2;
    00840     y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2;
    00841     v->x_pos = x;
    00842     v->y_pos = y;
    00843     v->z_pos = GetSlopeZ(x, y);
    00844
    00845     v->running_ticks = 0;
    00846
    00847     v->UpdateDeltaXY(v->direction);
    00848     v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
    00849
    00850     v->spritenum = svi->image_index;
    00851     v->cargo_type = svi->cargo_type;
    00852     v->cargo_subtype = 0;
    00853     v->cargo_cap = svi->capacity;
    00854     v->value = value.GetCost();
    00855
    00856     v->last_station_visited = INVALID_STATION;
    00857     v->max_speed = svi->max_speed;
    00858     v->engine_type = p1;
    00859
    00860     e = GetEngine(p1);
    00861     v->reliability = e->reliability;
    00862     v->reliability_spd_dec = e->reliability_spd_dec;
    00863     v->max_age = e->lifelength * 366;
    00864     _new_vehicle_id = v->index;
    00865
    00866     v->name = NULL;
    00867     v->u.ship.state = TRACK_BIT_DEPOT;
    00868
    00869     v->service_interval = _patches.servint_ships;
    00870     v->date_of_last_service = _date;
    00871     v->build_year = _cur_year;
    00872     v->cur_image = 0x0E5E;
    00873     v->random_bits = VehicleRandomBits();
    00874
    00875     v->vehicle_flags = 0;
    00876     if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
    00877
    00878     v->cargo_cap = GetVehicleProperty(v, 0x0D, svi->capacity);
    00879
    00880     VehiclePositionChanged(v);
    00881
    00882     InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
    00883     RebuildVehicleLists();
    00884     InvalidateWindow(WC_COMPANY, v->owner);
    00885     if (IsLocalPlayer())
    00886       InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Ship window
    00887
    00888     GetPlayer(_current_player)->num_engines[p1]++;
    00889   }
    00890
    00891   return value;
    00892 }
    00893
    00900 CommandCost CmdSellShip(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
    00901 {
    00902   Vehicle *v;
    00903
    00904   if (!IsValidVehicleID(p1)) return CMD_ERROR;
    00905
    00906   v = GetVehicle(p1);
    00907
    00908   if (v->type != VEH_SHIP || !CheckOwnership(v->owner)) return CMD_ERROR;
    00909
    00910   if (HASBITS(v->vehstatus, VS_CRASHED)) return_cmd_error(STR_CAN_T_SELL_DESTROYED_VEHICLE);
    00911
    00912   if (!v->IsStoppedInDepot()) {
    00913     return_cmd_error(STR_980B_SHIP_MUST_BE_STOPPED_IN);
    00914   }
    00915
    00916   CommandCost ret(EXPENSES_NEW_VEHICLES, -v->value);
    00917
    00918   if (flags & DC_EXEC) {
    00919     InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
    00920     RebuildVehicleLists();
    00921     InvalidateWindow(WC_COMPANY, v->owner);
    00922     DeleteWindowById(WC_VEHICLE_VIEW, v->index);
    00923     DeleteDepotHighlightOfVehicle(v);
    00924     delete v;
    00925   }
    00926
    00927   return ret;
    00928 }
    00929
    00936 CommandCost CmdStartStopShip(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
    00937 {
    00938   if (!IsValidVehicleID(p1)) return CMD_ERROR;
    00939
    00940   Vehicle *v = GetVehicle(p1);
    00941
    00942   if (v->type != VEH_SHIP || !CheckOwnership(v->owner)) return CMD_ERROR;
    00943
    00944   /* Check if this ship can be started/stopped. The callback will fail or
    00945    * return 0xFF if it can. */
    00946   uint16 callback = GetVehicleCallback(CBID_VEHICLE_START_STOP_CHECK, 0, 0, v->engine_type, v);
    00947   if (callback != CALLBACK_FAILED && GB(callback, 0, 8) != 0xFF) {
    00948     StringID error = GetGRFStringID(GetEngineGRFID(v->engine_type), 0xD000 + callback);
    00949     return_cmd_error(error);
    00950   }
    00951
    00952   if (flags & DC_EXEC) {
    00953     if (v->IsStoppedInDepot()) {
    00954       DeleteVehicleNews(p1, STR_981C_SHIP_IS_WAITING_IN_DEPOT);
    00955     }
    00956
    00957     v->vehstatus ^= VS_STOPPED;
    00958     v->cur_speed = 0;
    00959     InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    00960     InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
    00961     InvalidateWindowClasses(WC_SHIPS_LIST);
    00962   }
    00963
    00964   return CommandCost();
    00965 }
    00966
    00975 CommandCost CmdSendShipToDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
    00976 {
    00977   Vehicle *v;
    00978   const Depot *dep;
    00979
    00980   if (p2 & DEPOT_MASS_SEND) {
    00981     /* Mass goto depot requested */
    00982     if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR;
    00983     return SendAllVehiclesToDepot(VEH_SHIP, flags, p2 & DEPOT_SERVICE, _current_player, (p2 & VLW_MASK), p1);
    00984   }
    00985
    00986   if (!IsValidVehicleID(p1)) return CMD_ERROR;
    00987
    00988   v = GetVehicle(p1);
    00989
    00990   if (v->type != VEH_SHIP || !CheckOwnership(v->owner)) return CMD_ERROR;
    00991
    00992   if (v->vehstatus & VS_CRASHED) return CMD_ERROR;
    00993
    00994   if (v->IsInDepot()) return CMD_ERROR;
    00995
    00996   /* If the current orders are already goto-depot */
    00997   if (v->current_order.type == OT_GOTO_DEPOT) {
    00998     if (!!(p2 & DEPOT_SERVICE) == HasBit(v->current_order.flags, OF_HALT_IN_DEPOT)) {
    00999       /* We called with a different DEPOT_SERVICE setting.
    01000        * Now we change the setting to apply the new one and let the vehicle head for the same depot.
    01001        * Note: the if is (true for requesting service == true for ordered to stop in depot)          */
    01002       if (flags & DC_EXEC) {
    01003         ClrBit(v->current_order.flags, OF_PART_OF_ORDERS);
    01004         ToggleBit(v->current_order.flags, OF_HALT_IN_DEPOT);
    01005         InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    01006       }
    01007       return CommandCost();
    01008     }
    01009
    01010     if (p2 & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
    01011     if (flags & DC_EXEC) {
    01012       /* If the orders to 'goto depot' are in the orders list (forced servicing),
    01013        * then skip to the next order; effectively cancelling this forced service */
    01014       if (HasBit(v->current_order.flags, OF_PART_OF_ORDERS))
    01015         v->cur_order_index++;
    01016
    01017       v->current_order.type = OT_DUMMY;
    01018       v->current_order.flags = 0;
    01019       InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    01020     }
    01021     return CommandCost();
    01022   }
    01023
    01024   dep = FindClosestShipDepot(v);
    01025   if (dep == NULL) return_cmd_error(STR_981A_UNABLE_TO_FIND_LOCAL_DEPOT);
    01026
    01027   if (flags & DC_EXEC) {
    01028     if (v->current_order.type == OT_LOADING) v->LeaveStation();
    01029
    01030     v->dest_tile = dep->xy;
    01031     v->current_order.type = OT_GOTO_DEPOT;
    01032     v->current_order.flags = OFB_NON_STOP;
    01033     if (!(p2 & DEPOT_SERVICE)) SetBit(v->current_order.flags, OF_HALT_IN_DEPOT);
    01034     v->current_order.refit_cargo = CT_INVALID;
    01035     v->current_order.dest = dep->index;
    01036     InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    01037   }
    01038
    01039   return CommandCost();
    01040 }
    01041
    01042
    01053 CommandCost CmdRefitShip(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
    01054 {
    01055   Vehicle *v;
    01056   CommandCost cost(EXPENSES_SHIP_RUN);
    01057   CargoID new_cid = GB(p2, 0, 8); //gets the cargo number
    01058   byte new_subtype = GB(p2, 8, 8);
    01059   uint16 capacity = CALLBACK_FAILED;
    01060
    01061   if (!IsValidVehicleID(p1)) return CMD_ERROR;
    01062
    01063   v = GetVehicle(p1);
    01064
    01065   if (v->type != VEH_SHIP || !CheckOwnership(v->owner)) return CMD_ERROR;
    01066   if (!v->IsStoppedInDepot()) return_cmd_error(STR_980B_SHIP_MUST_BE_STOPPED_IN);
    01067   if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_CAN_T_REFIT_DESTROYED_VEHICLE);
    01068
    01069   /* Check cargo */
    01070   if (!ShipVehInfo(v->engine_type)->refittable) return CMD_ERROR;
    01071   if (new_cid >= NUM_CARGO || !CanRefitTo(v->engine_type, new_cid)) return CMD_ERROR;
    01072
    01073   /* Check the refit capacity callback */
    01074   if (HasBit(EngInfo(v->engine_type)->callbackmask, CBM_VEHICLE_REFIT_CAPACITY)) {
    01075     /* Back up the existing cargo type */
    01076     CargoID temp_cid = v->cargo_type;
    01077     byte temp_subtype = v->cargo_subtype;
    01078     v->cargo_type = new_cid;
    01079     v->cargo_subtype = new_subtype;
    01080
    01081     capacity = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
    01082
    01083     /* Restore the cargo type */
    01084     v->cargo_type = temp_cid;
    01085     v->cargo_subtype = temp_subtype;
    01086   }
    01087
    01088   if (capacity == CALLBACK_FAILED) {
    01089     capacity = GetVehicleProperty(v, 0x0D, ShipVehInfo(v->engine_type)->capacity);
    01090   }
    01091   _returned_refit_capacity = capacity;
    01092
    01093   if (IsHumanPlayer(v->owner) && new_cid != v->cargo_type) {
    01094     cost = GetRefitCost(v->engine_type);
    01095   }
    01096
    01097   if (flags & DC_EXEC) {
    01098     v->cargo_cap = capacity;
    01099     v->cargo.Truncate((v->cargo_type == new_cid) ? capacity : 0);
    01100     v->cargo_type = new_cid;
    01101     v->cargo_subtype = new_subtype;
    01102     InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
    01103     InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
    01104     RebuildVehicleLists();
    01105   }
    01106
    01107   return cost;
    01108
    01109 }