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 }

Generated on Wed Oct 1 17:03:23 2008 for openttd by  doxygen 1.5.6