ship_cmd.cpp

Go to the documentation of this file.
00001 /* $Id: ship_cmd.cpp 17160 2009-08-12 15:51:35Z rubidium $ */
00002 
00005 #include "stdafx.h"
00006 #include "ship.h"
00007 #include "landscape.h"
00008 #include "timetable.h"
00009 #include "command_func.h"
00010 #include "station_map.h"
00011 #include "news_func.h"
00012 #include "company_func.h"
00013 #include "npf.h"
00014 #include "depot_base.h"
00015 #include "vehicle_gui.h"
00016 #include "newgrf_engine.h"
00017 #include "yapf/yapf.h"
00018 #include "newgrf_sound.h"
00019 #include "spritecache.h"
00020 #include "strings_func.h"
00021 #include "functions.h"
00022 #include "window_func.h"
00023 #include "date_func.h"
00024 #include "vehicle_func.h"
00025 #include "sound_func.h"
00026 #include "variables.h"
00027 #include "autoreplace_gui.h"
00028 #include "gfx_func.h"
00029 #include "effectvehicle_func.h"
00030 #include "settings_type.h"
00031 #include "ai/ai.hpp"
00032 #include "pathfind.h"
00033 
00034 #include "table/strings.h"
00035 #include "table/sprites.h"
00036 
00037 static const uint16 _ship_sprites[] = {0x0E5D, 0x0E55, 0x0E65, 0x0E6D};
00038 
00039 static const TrackBits _ship_sometracks[4] = {
00040   TRACK_BIT_X | TRACK_BIT_LOWER | TRACK_BIT_LEFT,  // 0x19, // DIAGDIR_NE
00041   TRACK_BIT_Y | TRACK_BIT_UPPER | TRACK_BIT_LEFT,  // 0x16, // DIAGDIR_SE
00042   TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT, // 0x25, // DIAGDIR_SW
00043   TRACK_BIT_Y | TRACK_BIT_LOWER | TRACK_BIT_RIGHT, // 0x2A, // DIAGDIR_NW
00044 };
00045 
00046 static inline TrackBits GetTileShipTrackStatus(TileIndex tile)
00047 {
00048   return TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0));
00049 }
00050 
00051 static SpriteID GetShipIcon(EngineID engine)
00052 {
00053   uint8 spritenum = ShipVehInfo(engine)->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 = GetEngine(engine)->image_index;
00060   }
00061 
00062   return 6 + _ship_sprites[spritenum];
00063 }
00064 
00065 void DrawShipEngine(int x, int y, EngineID engine, SpriteID pal)
00066 {
00067   DrawSprite(GetShipIcon(engine), pal, x, y);
00068 }
00069 
00075 void GetShipSpriteSize(EngineID engine, uint &width, uint &height)
00076 {
00077   const Sprite *spr = GetSprite(GetShipIcon(engine), ST_NORMAL);
00078 
00079   width  = spr->width;
00080   height = spr->height;
00081 }
00082 
00083 SpriteID Ship::GetImage(Direction direction) const
00084 {
00085   uint8 spritenum = this->spritenum;
00086 
00087   if (is_custom_sprite(spritenum)) {
00088     SpriteID sprite = GetCustomVehicleSprite(this, direction);
00089     if (sprite != 0) return sprite;
00090 
00091     spritenum = GetEngine(this->engine_type)->image_index;
00092   }
00093 
00094   return _ship_sprites[spritenum] + direction;
00095 }
00096 
00097 static const Depot *FindClosestShipDepot(const Vehicle *v)
00098 {
00099   if (_settings_game.pf.pathfinder_for_ships == VPF_NPF) { // NPF is used
00100     Trackdir trackdir = GetVehicleTrackdir(v);
00101     NPFFoundTargetData ftd = NPFRouteToDepotTrialError(v->tile, trackdir, false, TRANSPORT_WATER, 0, v->owner, INVALID_RAILTYPES);
00102 
00103     if (ftd.best_bird_dist == 0) return GetDepotByTile(ftd.node.tile); // Found target
00104 
00105     return NULL; // Did not find target
00106   }
00107 
00108   /* OPF or YAPF - find the closest depot */
00109 
00110   const Depot *depot;
00111   const Depot *best_depot = NULL;
00112   uint best_dist = UINT_MAX;
00113 
00114   FOR_ALL_DEPOTS(depot) {
00115     TileIndex tile = depot->xy;
00116     if (IsShipDepotTile(tile) && IsTileOwner(tile, v->owner)) {
00117       uint dist = DistanceManhattan(tile, v->tile);
00118       if (dist < best_dist) {
00119         best_dist = dist;
00120         best_depot = depot;
00121       }
00122     }
00123   }
00124 
00125   return best_depot;
00126 }
00127 
00128 static void CheckIfShipNeedsService(Vehicle *v)
00129 {
00130   if (_settings_game.vehicle.servint_ships == 0 || !v->NeedsAutomaticServicing()) return;
00131   if (v->IsInDepot()) {
00132     VehicleServiceInDepot(v);
00133     return;
00134   }
00135 
00136   const Depot *depot = FindClosestShipDepot(v);
00137 
00138   if (depot == NULL || DistanceManhattan(v->tile, depot->xy) > 12) {
00139     if (v->current_order.IsType(OT_GOTO_DEPOT)) {
00140       v->current_order.MakeDummy();
00141       InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00142     }
00143     return;
00144   }
00145 
00146   v->current_order.MakeGoToDepot(depot->index, ODTFB_SERVICE);
00147   v->dest_tile = depot->xy;
00148   InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00149 }
00150 
00151 Money Ship::GetRunningCost() const
00152 {
00153   return GetVehicleProperty(this, 0x0F, ShipVehInfo(this->engine_type)->running_cost) * _price.ship_running;
00154 }
00155 
00156 void Ship::OnNewDay()
00157 {
00158   if ((++this->day_counter & 7) == 0)
00159     DecreaseVehicleValue(this);
00160 
00161   CheckVehicleBreakdown(this);
00162   AgeVehicle(this);
00163   CheckIfShipNeedsService(this);
00164 
00165   CheckOrders(this);
00166 
00167   if (this->running_ticks == 0) return;
00168 
00169   CommandCost cost(EXPENSES_SHIP_RUN, this->GetRunningCost() * this->running_ticks / (DAYS_IN_YEAR * DAY_TICKS));
00170 
00171   this->profit_this_year -= cost.GetCost();
00172   this->running_ticks = 0;
00173 
00174   SubtractMoneyFromCompanyFract(this->owner, cost);
00175 
00176   InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
00177   /* we need this for the profit */
00178   InvalidateWindowClasses(WC_SHIPS_LIST);
00179 }
00180 
00181 static void HandleBrokenShip(Vehicle *v)
00182 {
00183   if (v->breakdown_ctr != 1) {
00184     v->breakdown_ctr = 1;
00185     v->cur_speed = 0;
00186 
00187     if (v->breakdowns_since_last_service != 255)
00188       v->breakdowns_since_last_service++;
00189 
00190     InvalidateWindow(WC_VEHICLE_VIEW, v->index);
00191     InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00192 
00193     if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
00194       SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
00195         SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
00196     }
00197 
00198     if (!(v->vehstatus & VS_HIDDEN)) {
00199       Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
00200       if (u != NULL) u->u.effect.animation_state = v->breakdown_delay * 2;
00201     }
00202   }
00203 
00204   if (!(v->tick_counter & 1)) {
00205     if (!--v->breakdown_delay) {
00206       v->breakdown_ctr = 0;
00207       InvalidateWindow(WC_VEHICLE_VIEW, v->index);
00208     }
00209   }
00210 }
00211 
00212 void Ship::MarkDirty()
00213 {
00214   this->UpdateViewport(false, false);
00215 }
00216 
00217 static void PlayShipSound(const Vehicle *v)
00218 {
00219   if (!PlayVehicleSound(v, VSE_START)) {
00220     SndPlayVehicleFx(ShipVehInfo(v->engine_type)->sfx, v);
00221   }
00222 }
00223 
00224 void Ship::PlayLeaveStationSound() const
00225 {
00226   PlayShipSound(this);
00227 }
00228 
00229 TileIndex Ship::GetOrderStationLocation(StationID station)
00230 {
00231   if (station == this->last_station_visited) this->last_station_visited = INVALID_STATION;
00232 
00233   const Station *st = GetStation(station);
00234   if (st->dock_tile != INVALID_TILE) {
00235     return TILE_ADD(st->dock_tile, ToTileIndexDiff(GetDockOffset(st->dock_tile)));
00236   } else {
00237     this->cur_order_index++;
00238     return 0;
00239   }
00240 }
00241 
00242 void Ship::UpdateDeltaXY(Direction direction)
00243 {
00244 #define MKIT(a, b, c, d) ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF) << 0)
00245   static const uint32 _delta_xy_table[8] = {
00246     MKIT( 6,  6,  -3,  -3),
00247     MKIT( 6, 32,  -3, -16),
00248     MKIT( 6,  6,  -3,  -3),
00249     MKIT(32,  6, -16,  -3),
00250     MKIT( 6,  6,  -3,  -3),
00251     MKIT( 6, 32,  -3, -16),
00252     MKIT( 6,  6,  -3,  -3),
00253     MKIT(32,  6, -16,  -3),
00254   };
00255 #undef MKIT
00256 
00257   uint32 x = _delta_xy_table[direction];
00258   this->x_offs        = GB(x,  0, 8);
00259   this->y_offs        = GB(x,  8, 8);
00260   this->x_extent      = GB(x, 16, 8);
00261   this->y_extent      = GB(x, 24, 8);
00262   this->z_extent      = 6;
00263 }
00264 
00265 void RecalcShipStuff(Vehicle *v)
00266 {
00267   v->UpdateViewport(false, true);
00268   InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
00269 }
00270 
00271 static const TileIndexDiffC _ship_leave_depot_offs[] = {
00272   {-1,  0},
00273   { 0, -1}
00274 };
00275 
00276 static void CheckShipLeaveDepot(Vehicle *v)
00277 {
00278   if (!v->IsInDepot()) return;
00279 
00280   TileIndex tile = v->tile;
00281   Axis axis = GetShipDepotAxis(tile);
00282 
00283   /* Check first (north) side */
00284   if (_ship_sometracks[axis] & GetTileShipTrackStatus(TILE_ADD(tile, ToTileIndexDiff(_ship_leave_depot_offs[axis])))) {
00285     v->direction = ReverseDir(AxisToDirection(axis));
00286   /* Check second (south) side */
00287   } else if (_ship_sometracks[axis + 2] & GetTileShipTrackStatus(TILE_ADD(tile, -2 * ToTileIndexDiff(_ship_leave_depot_offs[axis])))) {
00288     v->direction = AxisToDirection(axis);
00289   } else {
00290     return;
00291   }
00292 
00293   v->u.ship.state = AxisToTrackBits(axis);
00294   v->vehstatus &= ~VS_HIDDEN;
00295 
00296   v->cur_speed = 0;
00297   RecalcShipStuff(v);
00298 
00299   PlayShipSound(v);
00300   VehicleServiceInDepot(v);
00301   InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
00302   InvalidateWindowClasses(WC_SHIPS_LIST);
00303 }
00304 
00305 static bool ShipAccelerate(Vehicle *v)
00306 {
00307   uint spd;
00308   byte t;
00309 
00310   spd = min(v->cur_speed + 1, GetVehicleProperty(v, 0x0B, v->max_speed));
00311 
00312   /* updates statusbar only if speed have changed to save CPU time */
00313   if (spd != v->cur_speed) {
00314     v->cur_speed = spd;
00315     if (_settings_client.gui.vehicle_speed)
00316       InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00317   }
00318 
00319   /* Decrease somewhat when turning */
00320   if (!(v->direction & 1)) spd = spd * 3 / 4;
00321 
00322   if (spd == 0) return false;
00323   if ((byte)++spd == 0) return true;
00324 
00325   v->progress = (t = v->progress) - (byte)spd;
00326 
00327   return (t < v->progress);
00328 }
00329 
00330 static void ShipArrivesAt(const Vehicle *v, Station *st)
00331 {
00332   /* Check if station was ever visited before */
00333   if (!(st->had_vehicle_of_type & HVOT_SHIP)) {
00334     st->had_vehicle_of_type |= HVOT_SHIP;
00335 
00336     SetDParam(0, st->index);
00337     AddNewsItem(
00338       STR_9833_CITIZENS_CELEBRATE_FIRST,
00339       (v->owner == _local_company) ? NS_ARRIVAL_COMPANY : NS_ARRIVAL_OTHER,
00340       v->index,
00341       st->index
00342     );
00343     AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
00344   }
00345 }
00346 
00347 struct PathFindShip {
00348   TileIndex skiptile;
00349   TileIndex dest_coords;
00350   uint best_bird_dist;
00351   uint best_length;
00352 };
00353 
00354 static bool ShipTrackFollower(TileIndex tile, PathFindShip *pfs, int track, uint length)
00355 {
00356   /* Found dest? */
00357   if (tile == pfs->dest_coords) {
00358     pfs->best_bird_dist = 0;
00359 
00360     pfs->best_length = minu(pfs->best_length, length);
00361     return true;
00362   }
00363 
00364   /* Skip this tile in the calculation */
00365   if (tile != pfs->skiptile) {
00366     pfs->best_bird_dist = minu(pfs->best_bird_dist, DistanceMaxPlusManhattan(pfs->dest_coords, tile));
00367   }
00368 
00369   return false;
00370 }
00371 
00372 static const byte _ship_search_directions[6][4] = {
00373   { 0, 9, 2, 9 },
00374   { 9, 1, 9, 3 },
00375   { 9, 0, 3, 9 },
00376   { 1, 9, 9, 2 },
00377   { 3, 2, 9, 9 },
00378   { 9, 9, 1, 0 },
00379 };
00380 
00381 static const byte _pick_shiptrack_table[6] = {1, 3, 2, 2, 0, 0};
00382 
00383 static uint FindShipTrack(Vehicle *v, TileIndex tile, DiagDirection dir, TrackBits bits, TileIndex skiptile, Track *track)
00384 {
00385   PathFindShip pfs;
00386   Track i, best_track;
00387   uint best_bird_dist = 0;
00388   uint best_length    = 0;
00389   uint r;
00390   byte ship_dir = v->direction & 3;
00391 
00392   pfs.dest_coords = v->dest_tile;
00393   pfs.skiptile = skiptile;
00394 
00395   best_track = INVALID_TRACK;
00396 
00397   do {
00398     i = RemoveFirstTrack(&bits);
00399 
00400     pfs.best_bird_dist = UINT_MAX;
00401     pfs.best_length = UINT_MAX;
00402 
00403     FollowTrack(tile, PATHFIND_FLAGS_SHIP_MODE | PATHFIND_FLAGS_DISABLE_TILE_HASH, TRANSPORT_WATER, 0, (DiagDirection)_ship_search_directions[i][dir], (TPFEnumProc*)ShipTrackFollower, NULL, &pfs);
00404 
00405     if (best_track != INVALID_TRACK) {
00406       if (pfs.best_bird_dist != 0) {
00407         /* neither reached the destination, pick the one with the smallest bird dist */
00408         if (pfs.best_bird_dist > best_bird_dist) goto bad;
00409         if (pfs.best_bird_dist < best_bird_dist) goto good;
00410       } else {
00411         if (pfs.best_length > best_length) goto bad;
00412         if (pfs.best_length < best_length) goto good;
00413       }
00414 
00415       /* if we reach this position, there's two paths of equal value so far.
00416        * pick one randomly. */
00417       r = GB(Random(), 0, 8);
00418       if (_pick_shiptrack_table[i] == ship_dir) r += 80;
00419       if (_pick_shiptrack_table[best_track] == ship_dir) r -= 80;
00420       if (r <= 127) goto bad;
00421     }
00422 good:;
00423     best_track = i;
00424     best_bird_dist = pfs.best_bird_dist;
00425     best_length = pfs.best_length;
00426 bad:;
00427 
00428   } while (bits != 0);
00429 
00430   *track = best_track;
00431   return best_bird_dist;
00432 }
00433 
00434 static inline NPFFoundTargetData PerfNPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, bool ignore_start_tile, NPFFindStationOrTileData *target, TransportType type, Owner owner, RailTypes railtypes)
00435 {
00436 
00437   void *perf = NpfBeginInterval();
00438   NPFFoundTargetData ret = NPFRouteToStationOrTile(tile, trackdir, ignore_start_tile, target, type, 0, owner, railtypes);
00439   int t = NpfEndInterval(perf);
00440   DEBUG(yapf, 4, "[NPFW] %d us - %d rounds - %d open - %d closed -- ", t, 0, _aystar_stats_open_size, _aystar_stats_closed_size);
00441   return ret;
00442 }
00443 
00447 static Track ChooseShipTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks)
00448 {
00449   assert(IsValidDiagDirection(enterdir));
00450 
00451   switch (_settings_game.pf.pathfinder_for_ships) {
00452     case VPF_YAPF: { // YAPF
00453       Trackdir trackdir = YapfChooseShipTrack(v, tile, enterdir, tracks);
00454       if (trackdir != INVALID_TRACKDIR) return TrackdirToTrack(trackdir);
00455     } break;
00456 
00457     case VPF_NPF: { // NPF
00458       NPFFindStationOrTileData fstd;
00459       Trackdir trackdir = GetVehicleTrackdir(v);
00460       assert(trackdir != INVALID_TRACKDIR); // Check that we are not in a depot
00461 
00462       NPFFillWithOrderData(&fstd, v);
00463 
00464       NPFFoundTargetData ftd = PerfNPFRouteToStationOrTile(tile - TileOffsByDiagDir(enterdir), trackdir, true, &fstd, TRANSPORT_WATER, v->owner, INVALID_RAILTYPES);
00465 
00466       /* If ftd.best_bird_dist is 0, we found our target and ftd.best_trackdir contains
00467        * the direction we need to take to get there, if ftd.best_bird_dist is not 0,
00468        * we did not find our target, but ftd.best_trackdir contains the direction leading
00469        * to the tile closest to our target. */
00470       if (ftd.best_trackdir != 0xff) return TrackdirToTrack(ftd.best_trackdir); // TODO: Wrapper function?
00471     } break;
00472 
00473     default:
00474     case VPF_OPF: { // OPF
00475       TileIndex tile2 = TILE_ADD(tile, -TileOffsByDiagDir(enterdir));
00476       Track track;
00477 
00478       /* Let's find out how far it would be if we would reverse first */
00479       TrackBits b = GetTileShipTrackStatus(tile2) & _ship_sometracks[ReverseDiagDir(enterdir)] & v->u.ship.state;
00480 
00481       uint distr = UINT_MAX; // distance if we reversed
00482       if (b != 0) {
00483         distr = FindShipTrack(v, tile2, ReverseDiagDir(enterdir), b, tile, &track);
00484         if (distr != UINT_MAX) distr++; // penalty for reversing
00485       }
00486 
00487       /* And if we would not reverse? */
00488       uint dist = FindShipTrack(v, tile, enterdir, tracks, 0, &track);
00489 
00490       if (dist <= distr) return track;
00491     } break;
00492   }
00493 
00494   return INVALID_TRACK; // We could better reverse
00495 }
00496 
00497 static const Direction _new_vehicle_direction_table[] = {
00498   DIR_N , DIR_NW, DIR_W , INVALID_DIR,
00499   DIR_NE, DIR_N , DIR_SW, INVALID_DIR,
00500   DIR_E , DIR_SE, DIR_S
00501 };
00502 
00503 static Direction ShipGetNewDirectionFromTiles(TileIndex new_tile, TileIndex old_tile)
00504 {
00505   uint offs = (TileY(new_tile) - TileY(old_tile) + 1) * 4 +
00506               TileX(new_tile) - TileX(old_tile) + 1;
00507   assert(offs < 11 && offs != 3 && offs != 7);
00508   return _new_vehicle_direction_table[offs];
00509 }
00510 
00511 static Direction ShipGetNewDirection(Vehicle *v, int x, int y)
00512 {
00513   uint offs = (y - v->y_pos + 1) * 4 + (x - v->x_pos + 1);
00514   assert(offs < 11 && offs != 3 && offs != 7);
00515   return _new_vehicle_direction_table[offs];
00516 }
00517 
00518 static inline TrackBits GetAvailShipTracks(TileIndex tile, int dir)
00519 {
00520   return GetTileShipTrackStatus(tile) & _ship_sometracks[dir];
00521 }
00522 
00523 static const byte _ship_subcoord[4][6][3] = {
00524   {
00525     {15, 8, 1},
00526     { 0, 0, 0},
00527     { 0, 0, 0},
00528     {15, 8, 2},
00529     {15, 7, 0},
00530     { 0, 0, 0},
00531   },
00532   {
00533     { 0, 0, 0},
00534     { 8, 0, 3},
00535     { 7, 0, 2},
00536     { 0, 0, 0},
00537     { 8, 0, 4},
00538     { 0, 0, 0},
00539   },
00540   {
00541     { 0, 8, 5},
00542     { 0, 0, 0},
00543     { 0, 7, 6},
00544     { 0, 0, 0},
00545     { 0, 0, 0},
00546     { 0, 8, 4},
00547   },
00548   {
00549     { 0, 0, 0},
00550     { 8, 15, 7},
00551     { 0, 0, 0},
00552     { 8, 15, 6},
00553     { 0, 0, 0},
00554     { 7, 15, 0},
00555   }
00556 };
00557 
00558 static void ShipController(Vehicle *v)
00559 {
00560   uint32 r;
00561   const byte *b;
00562   Direction dir;
00563   Track track;
00564   TrackBits tracks;
00565 
00566   v->tick_counter++;
00567   v->current_order_time++;
00568 
00569   if (v->breakdown_ctr != 0) {
00570     if (v->breakdown_ctr <= 2) {
00571       HandleBrokenShip(v);
00572       return;
00573     }
00574     if (!v->current_order.IsType(OT_LOADING)) v->breakdown_ctr--;
00575   }
00576 
00577   if (v->vehstatus & VS_STOPPED) return;
00578 
00579   ProcessOrders(v);
00580   v->HandleLoading();
00581 
00582   if (v->current_order.IsType(OT_LOADING)) return;
00583 
00584   CheckShipLeaveDepot(v);
00585 
00586   if (!ShipAccelerate(v)) return;
00587 
00588   GetNewVehiclePosResult gp = GetNewVehiclePos(v);
00589   if (v->u.ship.state != TRACK_BIT_WORMHOLE) {
00590     /* Not on a bridge */
00591     if (gp.old_tile == gp.new_tile) {
00592       /* Staying in tile */
00593       if (v->IsInDepot()) {
00594         gp.x = v->x_pos;
00595         gp.y = v->y_pos;
00596       } else {
00597         /* Not inside depot */
00598         r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
00599         if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;
00600 
00601         /* A leave station order only needs one tick to get processed, so we can
00602          * always skip ahead. */
00603         if (v->current_order.IsType(OT_LEAVESTATION)) {
00604           v->current_order.Free();
00605           InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00606         } else if (v->dest_tile != 0) {
00607           /* We have a target, let's see if we reached it... */
00608           if (v->current_order.IsType(OT_GOTO_STATION) &&
00609               GetStation(v->current_order.GetDestination())->IsBuoy() &&
00610               DistanceManhattan(v->dest_tile, gp.new_tile) <= 3) {
00611             /* We got within 3 tiles of our target buoy, so let's skip to our
00612              * next order */
00613             UpdateVehicleTimetable(v, true);
00614             v->cur_order_index++;
00615             v->current_order.MakeDummy();
00616             InvalidateVehicleOrder(v, 0);
00617           } else {
00618             /* Non-buoy orders really need to reach the tile */
00619             if (v->dest_tile == gp.new_tile) {
00620               if (v->current_order.IsType(OT_GOTO_DEPOT)) {
00621                 if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) {
00622                   VehicleEnterDepot(v);
00623                   return;
00624                 }
00625               } else if (v->current_order.IsType(OT_GOTO_STATION)) {
00626                 v->last_station_visited = v->current_order.GetDestination();
00627 
00628                 /* Process station in the orderlist. */
00629                 Station *st = GetStation(v->current_order.GetDestination());
00630                 if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations
00631                   ShipArrivesAt(v, st);
00632                   v->BeginLoading();
00633                 } else { // leave stations without docks right aways
00634                   v->current_order.MakeLeaveStation();
00635                   v->cur_order_index++;
00636                   InvalidateVehicleOrder(v, 0);
00637                 }
00638               }
00639             }
00640           }
00641         }
00642       }
00643     } else {
00644       DiagDirection diagdir;
00645       /* New tile */
00646       if (TileX(gp.new_tile) >= MapMaxX() || TileY(gp.new_tile) >= MapMaxY()) {
00647         goto reverse_direction;
00648       }
00649 
00650       dir = ShipGetNewDirectionFromTiles(gp.new_tile, gp.old_tile);
00651       assert(dir == DIR_NE || dir == DIR_SE || dir == DIR_SW || dir == DIR_NW);
00652       diagdir = DirToDiagDir(dir);
00653       tracks = GetAvailShipTracks(gp.new_tile, diagdir);
00654       if (tracks == TRACK_BIT_NONE) goto reverse_direction;
00655 
00656       /* Choose a direction, and continue if we find one */
00657       track = ChooseShipTrack(v, gp.new_tile, diagdir, tracks);
00658       if (track == INVALID_TRACK) goto reverse_direction;
00659 
00660       b = _ship_subcoord[diagdir][track];
00661 
00662       gp.x = (gp.x & ~0xF) | b[0];
00663       gp.y = (gp.y & ~0xF) | b[1];
00664 
00665       /* Call the landscape function and tell it that the vehicle entered the tile */
00666       r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
00667       if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;
00668 
00669       if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
00670         v->tile = gp.new_tile;
00671         v->u.ship.state = TrackToTrackBits(track);
00672       }
00673 
00674       v->direction = (Direction)b[2];
00675     }
00676   } else {
00677     /* On a bridge */
00678     if (!IsTileType(gp.new_tile, MP_TUNNELBRIDGE) || !HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
00679       v->x_pos = gp.x;
00680       v->y_pos = gp.y;
00681       VehicleMove(v, !(v->vehstatus & VS_HIDDEN));
00682       return;
00683     }
00684   }
00685 
00686   /* update image of ship, as well as delta XY */
00687   dir = ShipGetNewDirection(v, gp.x, gp.y);
00688   v->x_pos = gp.x;
00689   v->y_pos = gp.y;
00690   v->z_pos = GetSlopeZ(gp.x, gp.y);
00691 
00692 getout:
00693   v->UpdateViewport(true, true);
00694   return;
00695 
00696 reverse_direction:
00697   dir = ReverseDir(v->direction);
00698   v->direction = dir;
00699   goto getout;
00700 }
00701 
00702 static void AgeShipCargo(Vehicle *v)
00703 {
00704   if (_age_cargo_skip_counter != 0) return;
00705   v->cargo.AgeCargo();
00706 }
00707 
00708 void Ship::Tick()
00709 {
00710   if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
00711 
00712   AgeShipCargo(this);
00713   ShipController(this);
00714 }
00715 
00722 CommandCost CmdBuildShip(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00723 {
00724   UnitID unit_num;
00725 
00726   if (!IsEngineBuildable(p1, VEH_SHIP, _current_company)) return_cmd_error(STR_SHIP_NOT_AVAILABLE);
00727 
00728   const Engine *e = GetEngine(p1);
00729   CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost());
00730 
00731   /* Engines without valid cargo should not be available */
00732   if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR;
00733 
00734   if (flags & DC_QUERY_COST) return value;
00735 
00736   /* The ai_new queries the vehicle cost before building the route,
00737    * so we must check against cheaters no sooner than now. --pasky */
00738   if (!IsShipDepotTile(tile)) return CMD_ERROR;
00739   if (!IsTileOwner(tile, _current_company)) return CMD_ERROR;
00740 
00741   unit_num = (flags & DC_AUTOREPLACE) ? 0 : GetFreeUnitNumber(VEH_SHIP);
00742 
00743   if (!Vehicle::CanAllocateItem() || unit_num > _settings_game.vehicle.max_ships)
00744     return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
00745 
00746   if (flags & DC_EXEC) {
00747     int x;
00748     int y;
00749 
00750     const ShipVehicleInfo *svi = ShipVehInfo(p1);
00751 
00752     Vehicle *v = new Ship();
00753     v->unitnumber = unit_num;
00754 
00755     v->owner = _current_company;
00756     v->tile = tile;
00757     x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2;
00758     y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2;
00759     v->x_pos = x;
00760     v->y_pos = y;
00761     v->z_pos = GetSlopeZ(x, y);
00762 
00763     v->running_ticks = 0;
00764 
00765     v->UpdateDeltaXY(v->direction);
00766     v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
00767 
00768     v->spritenum = svi->image_index;
00769     v->cargo_type = e->GetDefaultCargoType();
00770     v->cargo_subtype = 0;
00771     v->cargo_cap = svi->capacity;
00772     v->value = value.GetCost();
00773 
00774     v->last_station_visited = INVALID_STATION;
00775     v->max_speed = svi->max_speed;
00776     v->engine_type = p1;
00777 
00778     v->reliability = e->reliability;
00779     v->reliability_spd_dec = e->reliability_spd_dec;
00780     v->max_age = e->lifelength * DAYS_IN_LEAP_YEAR;
00781     _new_vehicle_id = v->index;
00782 
00783     v->name = NULL;
00784     v->u.ship.state = TRACK_BIT_DEPOT;
00785 
00786     v->service_interval = _settings_game.vehicle.servint_ships;
00787     v->date_of_last_service = _date;
00788     v->build_year = _cur_year;
00789     v->cur_image = 0x0E5E;
00790     v->random_bits = VehicleRandomBits();
00791 
00792     v->vehicle_flags = 0;
00793     if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
00794 
00795     v->InvalidateNewGRFCacheOfChain();
00796 
00797     v->cargo_cap = GetVehicleProperty(v, 0x0D, svi->capacity);
00798 
00799     v->InvalidateNewGRFCacheOfChain();
00800 
00801     VehicleMove(v, false);
00802 
00803     InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
00804     InvalidateWindowClassesData(WC_SHIPS_LIST, 0);
00805     InvalidateWindow(WC_COMPANY, v->owner);
00806     if (IsLocalCompany())
00807       InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Ship window
00808 
00809     GetCompany(_current_company)->num_engines[p1]++;
00810   }
00811 
00812   return value;
00813 }
00814 
00821 CommandCost CmdSellShip(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00822 {
00823   Vehicle *v;
00824 
00825   if (!IsValidVehicleID(p1)) return CMD_ERROR;
00826 
00827   v = GetVehicle(p1);
00828 
00829   if (v->type != VEH_SHIP || !CheckOwnership(v->owner)) return CMD_ERROR;
00830 
00831   if (HASBITS(v->vehstatus, VS_CRASHED)) return_cmd_error(STR_CAN_T_SELL_DESTROYED_VEHICLE);
00832 
00833   if (!v->IsStoppedInDepot()) {
00834     return_cmd_error(STR_980B_SHIP_MUST_BE_STOPPED_IN);
00835   }
00836 
00837   CommandCost ret(EXPENSES_NEW_VEHICLES, -v->value);
00838 
00839   if (flags & DC_EXEC) {
00840     delete v;
00841   }
00842 
00843   return ret;
00844 }
00845 
00846 bool Ship::FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse)
00847 {
00848   const Depot *depot = FindClosestShipDepot(this);
00849 
00850   if (depot == NULL) return false;
00851 
00852   if (location    != NULL) *location    = depot->xy;
00853   if (destination != NULL) *destination = depot->index;
00854 
00855   return true;
00856 }
00857 
00866 CommandCost CmdSendShipToDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00867 {
00868   if (p2 & DEPOT_MASS_SEND) {
00869     /* Mass goto depot requested */
00870     if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR;
00871     return SendAllVehiclesToDepot(VEH_SHIP, flags, p2 & DEPOT_SERVICE, _current_company, (p2 & VLW_MASK), p1);
00872   }
00873 
00874   if (!IsValidVehicleID(p1)) return CMD_ERROR;
00875 
00876   Vehicle *v = GetVehicle(p1);
00877 
00878   if (v->type != VEH_SHIP) return CMD_ERROR;
00879 
00880   return v->SendToDepot(flags, (DepotCommand)(p2 & DEPOT_COMMAND_MASK));
00881 }
00882 
00883 
00894 CommandCost CmdRefitShip(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00895 {
00896   Vehicle *v;
00897   CommandCost cost(EXPENSES_SHIP_RUN);
00898   CargoID new_cid = GB(p2, 0, 8); // gets the cargo number
00899   byte new_subtype = GB(p2, 8, 8);
00900   uint16 capacity = CALLBACK_FAILED;
00901 
00902   if (!IsValidVehicleID(p1)) return CMD_ERROR;
00903 
00904   v = GetVehicle(p1);
00905 
00906   if (v->type != VEH_SHIP || !CheckOwnership(v->owner)) return CMD_ERROR;
00907   if (!v->IsStoppedInDepot()) return_cmd_error(STR_980B_SHIP_MUST_BE_STOPPED_IN);
00908   if (v->vehstatus & VS_CRASHED) return_cmd_error(STR_CAN_T_REFIT_DESTROYED_VEHICLE);
00909 
00910   /* Check cargo */
00911   if (!ShipVehInfo(v->engine_type)->refittable) return CMD_ERROR;
00912   if (new_cid >= NUM_CARGO || !CanRefitTo(v->engine_type, new_cid)) return CMD_ERROR;
00913 
00914   /* Check the refit capacity callback */
00915   if (HasBit(EngInfo(v->engine_type)->callbackmask, CBM_VEHICLE_REFIT_CAPACITY)) {
00916     /* Back up the existing cargo type */
00917     CargoID temp_cid = v->cargo_type;
00918     byte temp_subtype = v->cargo_subtype;
00919     v->cargo_type = new_cid;
00920     v->cargo_subtype = new_subtype;
00921 
00922     capacity = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
00923 
00924     /* Restore the cargo type */
00925     v->cargo_type = temp_cid;
00926     v->cargo_subtype = temp_subtype;
00927   }
00928 
00929   if (capacity == CALLBACK_FAILED) {
00930     capacity = GetVehicleProperty(v, 0x0D, ShipVehInfo(v->engine_type)->capacity);
00931   }
00932   _returned_refit_capacity = capacity;
00933 
00934   if (new_cid != v->cargo_type) {
00935     cost = GetRefitCost(v->engine_type);
00936   }
00937 
00938   if (flags & DC_EXEC) {
00939     v->cargo_cap = capacity;
00940     v->cargo.Truncate((v->cargo_type == new_cid) ? capacity : 0);
00941     v->cargo_type = new_cid;
00942     v->cargo_subtype = new_subtype;
00943     v->colourmap = PAL_NONE; // invalidate vehicle colour map
00944     v->InvalidateNewGRFCacheOfChain();
00945     InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
00946     InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
00947     InvalidateWindowClassesData(WC_SHIPS_LIST, 0);
00948   }
00949 
00950   return cost;
00951 
00952 }

Generated on Sun Sep 13 08:19:19 2009 for OpenTTD by  doxygen 1.5.6