00001
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,
00047 TRACK_BIT_Y | TRACK_BIT_UPPER | TRACK_BIT_LEFT,
00048 TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT,
00049 TRACK_BIT_Y | TRACK_BIT_LOWER | TRACK_BIT_RIGHT,
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) {
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);
00119
00120 return NULL;
00121 }
00122
00123
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
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
00353 if (_ship_sometracks[axis] & GetTileShipTrackStatus(TILE_ADD(tile, ToTileIndexDiff(_ship_leave_depot_offs[axis])))) {
00354 m = (axis == AXIS_X) ? 0x101 : 0x207;
00355
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
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
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
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
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
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
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
00491
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: {
00528 Trackdir trackdir = YapfChooseShipTrack(v, tile, enterdir, tracks);
00529 if (trackdir != INVALID_TRACKDIR) return TrackdirToTrack(trackdir);
00530 } break;
00531
00532 case VPF_NPF: {
00533 NPFFindStationOrTileData fstd;
00534 Trackdir trackdir = GetVehicleTrackdir(v);
00535 assert(trackdir != INVALID_TRACKDIR);
00536
00537 NPFFillWithOrderData(&fstd, v);
00538
00539 NPFFoundTargetData ftd = PerfNPFRouteToStationOrTile(tile - TileOffsByDiagDir(enterdir), trackdir, true, &fstd, TRANSPORT_WATER, v->owner, INVALID_RAILTYPES);
00540
00541
00542
00543
00544
00545 if (ftd.best_trackdir != 0xff) return TrackdirToTrack(ftd.best_trackdir);
00546 } break;
00547
00548 default:
00549 case VPF_OPF: {
00550 TileIndex tile2 = TILE_ADD(tile, -TileOffsByDiagDir(enterdir));
00551 Track track;
00552
00553
00554 TrackBits b = GetTileShipTrackStatus(tile2) & _ship_sometracks[ReverseDiagDir(enterdir)] & v->u.ship.state;
00555
00556 uint distr = UINT_MAX;
00557 if (b != 0) {
00558 distr = FindShipTrack(v, tile2, ReverseDiagDir(enterdir), b, tile, &track);
00559 if (distr != UINT_MAX) distr++;
00560 }
00561
00562
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;
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
00668 if (v->IsInDepot()) {
00669 gp.x = v->x_pos;
00670 gp.y = v->y_pos;
00671 } else {
00672
00673 r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
00674 if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;
00675
00676
00677
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
00683 if (v->current_order.type == OT_GOTO_STATION &&
00684 IsBuoyTile(v->dest_tile) &&
00685 DistanceManhattan(v->dest_tile, gp.new_tile) <= 3) {
00686
00687
00688 UpdateVehicleTimetable(v, true);
00689 v->cur_order_index++;
00690 v->current_order.type = OT_DUMMY;
00691 InvalidateVehicleOrder(v);
00692 } else {
00693
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
00706 st = GetStation(v->current_order.dest);
00707 if (st->facilities & FACIL_DOCK) {
00708 ShipArrivesAt(v, st);
00709 v->BeginLoading();
00710 } else {
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
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
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
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
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
00819
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);
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
00945
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
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
00997 if (v->current_order.type == OT_GOTO_DEPOT) {
00998 if (!!(p2 & DEPOT_SERVICE) == HasBit(v->current_order.flags, OF_HALT_IN_DEPOT)) {
00999
01000
01001
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;
01011 if (flags & DC_EXEC) {
01012
01013
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);
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
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
01074 if (HasBit(EngInfo(v->engine_type)->callbackmask, CBM_VEHICLE_REFIT_CAPACITY)) {
01075
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
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 }