OpenTTD
ship_cmd.cpp
Go to the documentation of this file.
1 /* $Id: ship_cmd.cpp 27134 2015-02-01 20:54:24Z frosch $ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8  */
9 
12 #include "stdafx.h"
13 #include "ship.h"
14 #include "landscape.h"
15 #include "timetable.h"
16 #include "news_func.h"
17 #include "company_func.h"
19 #include "depot_base.h"
20 #include "station_base.h"
21 #include "newgrf_engine.h"
22 #include "pathfinder/yapf/yapf.h"
23 #include "newgrf_sound.h"
24 #include "spritecache.h"
25 #include "strings_func.h"
26 #include "window_func.h"
27 #include "date_func.h"
28 #include "vehicle_func.h"
29 #include "sound_func.h"
30 #include "ai/ai.hpp"
32 #include "engine_base.h"
33 #include "company_base.h"
34 #include "tunnelbridge_map.h"
35 #include "zoom_func.h"
36 
37 #include "table/strings.h"
38 
39 #include "safeguards.h"
40 
47 {
48  if (HasTileWaterClass(tile)) return GetWaterClass(tile);
49  if (IsTileType(tile, MP_TUNNELBRIDGE)) {
51  return WATER_CLASS_CANAL;
52  }
53  if (IsTileType(tile, MP_RAILWAY)) {
54  assert(GetRailGroundType(tile) == RAIL_GROUND_WATER);
55  return WATER_CLASS_SEA;
56  }
57  NOT_REACHED();
58 }
59 
60 static const uint16 _ship_sprites[] = {0x0E5D, 0x0E55, 0x0E65, 0x0E6D};
61 
62 template <>
63 bool IsValidImageIndex<VEH_SHIP>(uint8 image_index)
64 {
65  return image_index < lengthof(_ship_sprites);
66 }
67 
68 static inline TrackBits GetTileShipTrackStatus(TileIndex tile)
69 {
71 }
72 
73 static SpriteID GetShipIcon(EngineID engine, EngineImageType image_type)
74 {
75  const Engine *e = Engine::Get(engine);
76  uint8 spritenum = e->u.ship.image_index;
77 
78  if (is_custom_sprite(spritenum)) {
79  SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W, image_type);
80  if (sprite != 0) return sprite;
81 
82  spritenum = e->original_image_index;
83  }
84 
85  assert(IsValidImageIndex<VEH_SHIP>(spritenum));
86  return DIR_W + _ship_sprites[spritenum];
87 }
88 
89 void DrawShipEngine(int left, int right, int preferred_x, int y, EngineID engine, PaletteID pal, EngineImageType image_type)
90 {
91  SpriteID sprite = GetShipIcon(engine, image_type);
92  const Sprite *real_sprite = GetSprite(sprite, ST_NORMAL);
93  preferred_x = Clamp(preferred_x,
94  left - UnScaleGUI(real_sprite->x_offs),
95  right - UnScaleGUI(real_sprite->width) - UnScaleGUI(real_sprite->x_offs));
96  DrawSprite(sprite, pal, preferred_x, y);
97 }
98 
108 void GetShipSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs, int &yoffs, EngineImageType image_type)
109 {
110  const Sprite *spr = GetSprite(GetShipIcon(engine, image_type), ST_NORMAL);
111 
112  width = UnScaleGUI(spr->width);
113  height = UnScaleGUI(spr->height);
114  xoffs = UnScaleGUI(spr->x_offs);
115  yoffs = UnScaleGUI(spr->y_offs);
116 }
117 
118 SpriteID Ship::GetImage(Direction direction, EngineImageType image_type) const
119 {
120  uint8 spritenum = this->spritenum;
121 
122  if (is_custom_sprite(spritenum)) {
123  SpriteID sprite = GetCustomVehicleSprite(this, direction, image_type);
124  if (sprite != 0) return sprite;
125 
126  spritenum = this->GetEngine()->original_image_index;
127  }
128 
129  assert(IsValidImageIndex<VEH_SHIP>(spritenum));
130  return _ship_sprites[spritenum] + direction;
131 }
132 
133 static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance)
134 {
135  /* Find the closest depot */
136  const Depot *depot;
137  const Depot *best_depot = NULL;
138  /* If we don't have a maximum distance, i.e. distance = 0,
139  * we want to find any depot so the best distance of no
140  * depot must be more than any correct distance. On the
141  * other hand if we have set a maximum distance, any depot
142  * further away than max_distance can safely be ignored. */
143  uint best_dist = max_distance == 0 ? UINT_MAX : max_distance + 1;
144 
145  FOR_ALL_DEPOTS(depot) {
146  TileIndex tile = depot->xy;
147  if (IsShipDepotTile(tile) && IsTileOwner(tile, v->owner)) {
148  uint dist = DistanceManhattan(tile, v->tile);
149  if (dist < best_dist) {
150  best_dist = dist;
151  best_depot = depot;
152  }
153  }
154  }
155 
156  return best_depot;
157 }
158 
159 static void CheckIfShipNeedsService(Vehicle *v)
160 {
161  if (Company::Get(v->owner)->settings.vehicle.servint_ships == 0 || !v->NeedsAutomaticServicing()) return;
162  if (v->IsChainInDepot()) {
164  return;
165  }
166 
167  uint max_distance;
169  case VPF_OPF: max_distance = 12; break;
172  default: NOT_REACHED();
173  }
174 
175  const Depot *depot = FindClosestShipDepot(v, max_distance);
176 
177  if (depot == NULL) {
178  if (v->current_order.IsType(OT_GOTO_DEPOT)) {
181  }
182  return;
183  }
184 
186  v->dest_tile = depot->xy;
188 }
189 
194 {
195  const ShipVehicleInfo *svi = ShipVehInfo(this->engine_type);
196 
197  /* Get speed fraction for the current water type. Aqueducts are always canals. */
198  bool is_ocean = GetEffectiveWaterClass(this->tile) == WATER_CLASS_SEA;
199  uint raw_speed = GetVehicleProperty(this, PROP_SHIP_SPEED, svi->max_speed);
200  this->vcache.cached_max_speed = svi->ApplyWaterClassSpeedFrac(raw_speed, is_ocean);
201 
202  /* Update cargo aging period. */
203  this->vcache.cached_cargo_age_period = GetVehicleProperty(this, PROP_SHIP_CARGO_AGE_PERIOD, EngInfo(this->engine_type)->cargo_age_period);
204 
205  this->UpdateVisualEffect();
206 }
207 
209 {
210  const Engine *e = this->GetEngine();
211  uint cost_factor = GetVehicleProperty(this, PROP_SHIP_RUNNING_COST_FACTOR, e->u.ship.running_cost);
212  return GetPrice(PR_RUNNING_SHIP, cost_factor, e->GetGRF());
213 }
214 
216 {
217  if ((++this->day_counter & 7) == 0) {
218  DecreaseVehicleValue(this);
219  }
220 
221  CheckVehicleBreakdown(this);
222  AgeVehicle(this);
223  CheckIfShipNeedsService(this);
224 
225  CheckOrders(this);
226 
227  if (this->running_ticks == 0) return;
228 
230 
231  this->profit_this_year -= cost.GetCost();
232  this->running_ticks = 0;
233 
235 
237  /* we need this for the profit */
239 }
240 
242 {
243  if (this->vehstatus & VS_CRASHED) return INVALID_TRACKDIR;
244 
245  if (this->IsInDepot()) {
246  /* We'll assume the ship is facing outwards */
247  return DiagDirToDiagTrackdir(GetShipDepotDirection(this->tile));
248  }
249 
250  if (this->state == TRACK_BIT_WORMHOLE) {
251  /* ship on aqueduct, so just use his direction and assume a diagonal track */
253  }
254 
256 }
257 
259 {
260  this->colourmap = PAL_NONE;
261  this->UpdateViewport(true, false);
262  this->UpdateCache();
263 }
264 
265 static void PlayShipSound(const Vehicle *v)
266 {
267  if (!PlayVehicleSound(v, VSE_START)) {
268  SndPlayVehicleFx(ShipVehInfo(v->engine_type)->sfx, v);
269  }
270 }
271 
273 {
274  PlayShipSound(this);
275 }
276 
278 {
279  if (station == this->last_station_visited) this->last_station_visited = INVALID_STATION;
280 
281  const Station *st = Station::Get(station);
282  if (st->dock_tile != INVALID_TILE) {
284  } else {
285  this->IncrementRealOrderIndex();
286  return 0;
287  }
288 }
289 
291 {
292  static const int8 _delta_xy_table[8][4] = {
293  /* y_extent, x_extent, y_offs, x_offs */
294  { 6, 6, -3, -3}, // N
295  { 6, 32, -3, -16}, // NE
296  { 6, 6, -3, -3}, // E
297  {32, 6, -16, -3}, // SE
298  { 6, 6, -3, -3}, // S
299  { 6, 32, -3, -16}, // SW
300  { 6, 6, -3, -3}, // W
301  {32, 6, -16, -3}, // NW
302  };
303 
304  const int8 *bb = _delta_xy_table[direction];
305  this->x_offs = bb[3];
306  this->y_offs = bb[2];
307  this->x_extent = bb[1];
308  this->y_extent = bb[0];
309  this->z_extent = 6;
310 }
311 
312 static const TileIndexDiffC _ship_leave_depot_offs[] = {
313  {-1, 0},
314  { 0, -1}
315 };
316 
317 static bool CheckShipLeaveDepot(Ship *v)
318 {
319  if (!v->IsChainInDepot()) return false;
320 
321  /* We are leaving a depot, but have to go to the exact same one; re-enter */
322  if (v->current_order.IsType(OT_GOTO_DEPOT) &&
325  return true;
326  }
327 
328  TileIndex tile = v->tile;
329  Axis axis = GetShipDepotAxis(tile);
330 
331  DiagDirection north_dir = ReverseDiagDir(AxisToDiagDir(axis));
332  TileIndex north_neighbour = TILE_ADD(tile, ToTileIndexDiff(_ship_leave_depot_offs[axis]));
333  DiagDirection south_dir = AxisToDiagDir(axis);
334  TileIndex south_neighbour = TILE_ADD(tile, -2 * ToTileIndexDiff(_ship_leave_depot_offs[axis]));
335 
336  TrackBits north_tracks = DiagdirReachesTracks(north_dir) & GetTileShipTrackStatus(north_neighbour);
337  TrackBits south_tracks = DiagdirReachesTracks(south_dir) & GetTileShipTrackStatus(south_neighbour);
338  if (north_tracks && south_tracks) {
339  /* Ask pathfinder for best direction */
340  bool reverse = false;
341  bool path_found;
343  case VPF_OPF: reverse = OPFShipChooseTrack(v, north_neighbour, north_dir, north_tracks, path_found) == INVALID_TRACK; break; // OPF always allows reversing
344  case VPF_NPF: reverse = NPFShipCheckReverse(v); break;
345  case VPF_YAPF: reverse = YapfShipCheckReverse(v); break;
346  default: NOT_REACHED();
347  }
348  if (reverse) north_tracks = TRACK_BIT_NONE;
349  }
350 
351  if (north_tracks) {
352  /* Leave towards north */
353  v->direction = DiagDirToDir(north_dir);
354  } else if (south_tracks) {
355  /* Leave towards south */
356  v->direction = DiagDirToDir(south_dir);
357  } else {
358  /* Both ways blocked */
359  return false;
360  }
361 
362  v->state = AxisToTrackBits(axis);
363  v->vehstatus &= ~VS_HIDDEN;
364 
365  v->cur_speed = 0;
366  v->UpdateViewport(true, true);
368 
369  PlayShipSound(v);
373 
374  return false;
375 }
376 
377 static bool ShipAccelerate(Vehicle *v)
378 {
379  uint spd;
380  byte t;
381 
382  spd = min(v->cur_speed + 1, v->vcache.cached_max_speed);
383  spd = min(spd, v->current_order.GetMaxSpeed() * 2);
384 
385  /* updates statusbar only if speed have changed to save CPU time */
386  if (spd != v->cur_speed) {
387  v->cur_speed = spd;
389  }
390 
391  /* Convert direction-independent speed into direction-dependent speed. (old movement method) */
392  spd = v->GetOldAdvanceSpeed(spd);
393 
394  if (spd == 0) return false;
395  if ((byte)++spd == 0) return true;
396 
397  v->progress = (t = v->progress) - (byte)spd;
398 
399  return (t < v->progress);
400 }
401 
407 static void ShipArrivesAt(const Vehicle *v, Station *st)
408 {
409  /* Check if station was ever visited before */
410  if (!(st->had_vehicle_of_type & HVOT_SHIP)) {
411  st->had_vehicle_of_type |= HVOT_SHIP;
412 
413  SetDParam(0, st->index);
415  STR_NEWS_FIRST_SHIP_ARRIVAL,
417  v->index,
418  st->index
419  );
420  AI::NewEvent(v->owner, new ScriptEventStationFirstVehicle(st->index, v->index));
421  }
422 }
423 
424 
434 static Track ChooseShipTrack(Ship *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks)
435 {
436  assert(IsValidDiagDirection(enterdir));
437 
438  bool path_found = true;
439  Track track;
441  case VPF_OPF: track = OPFShipChooseTrack(v, tile, enterdir, tracks, path_found); break;
442  case VPF_NPF: track = NPFShipChooseTrack(v, tile, enterdir, tracks, path_found); break;
443  case VPF_YAPF: track = YapfShipChooseTrack(v, tile, enterdir, tracks, path_found); break;
444  default: NOT_REACHED();
445  }
446 
447  v->HandlePathfindingResult(path_found);
448  return track;
449 }
450 
451 static const Direction _new_vehicle_direction_table[] = {
454  DIR_E , DIR_SE, DIR_S
455 };
456 
457 static Direction ShipGetNewDirectionFromTiles(TileIndex new_tile, TileIndex old_tile)
458 {
459  uint offs = (TileY(new_tile) - TileY(old_tile) + 1) * 4 +
460  TileX(new_tile) - TileX(old_tile) + 1;
461  assert(offs < 11 && offs != 3 && offs != 7);
462  return _new_vehicle_direction_table[offs];
463 }
464 
465 static Direction ShipGetNewDirection(Vehicle *v, int x, int y)
466 {
467  uint offs = (y - v->y_pos + 1) * 4 + (x - v->x_pos + 1);
468  assert(offs < 11 && offs != 3 && offs != 7);
469  return _new_vehicle_direction_table[offs];
470 }
471 
472 static inline TrackBits GetAvailShipTracks(TileIndex tile, DiagDirection dir)
473 {
474  return GetTileShipTrackStatus(tile) & DiagdirReachesTracks(dir);
475 }
476 
477 static const byte _ship_subcoord[4][6][3] = {
478  {
479  {15, 8, 1},
480  { 0, 0, 0},
481  { 0, 0, 0},
482  {15, 8, 2},
483  {15, 7, 0},
484  { 0, 0, 0},
485  },
486  {
487  { 0, 0, 0},
488  { 8, 0, 3},
489  { 7, 0, 2},
490  { 0, 0, 0},
491  { 8, 0, 4},
492  { 0, 0, 0},
493  },
494  {
495  { 0, 8, 5},
496  { 0, 0, 0},
497  { 0, 7, 6},
498  { 0, 0, 0},
499  { 0, 0, 0},
500  { 0, 8, 4},
501  },
502  {
503  { 0, 0, 0},
504  { 8, 15, 7},
505  { 0, 0, 0},
506  { 8, 15, 6},
507  { 0, 0, 0},
508  { 7, 15, 0},
509  }
510 };
511 
512 static void ShipController(Ship *v)
513 {
514  uint32 r;
515  const byte *b;
516  Direction dir;
517  Track track;
518  TrackBits tracks;
519 
520  v->tick_counter++;
521  v->current_order_time++;
522 
523  if (v->HandleBreakdown()) return;
524 
525  if (v->vehstatus & VS_STOPPED) return;
526 
527  ProcessOrders(v);
528  v->HandleLoading();
529 
530  if (v->current_order.IsType(OT_LOADING)) return;
531 
532  if (CheckShipLeaveDepot(v)) return;
533 
534  v->ShowVisualEffect();
535 
536  if (!ShipAccelerate(v)) return;
537 
539  if (v->state != TRACK_BIT_WORMHOLE) {
540  /* Not on a bridge */
541  if (gp.old_tile == gp.new_tile) {
542  /* Staying in tile */
543  if (v->IsInDepot()) {
544  gp.x = v->x_pos;
545  gp.y = v->y_pos;
546  } else {
547  /* Not inside depot */
548  r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
549  if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;
550 
551  /* A leave station order only needs one tick to get processed, so we can
552  * always skip ahead. */
553  if (v->current_order.IsType(OT_LEAVESTATION)) {
554  v->current_order.Free();
556  } else if (v->dest_tile != 0) {
557  /* We have a target, let's see if we reached it... */
558  if (v->current_order.IsType(OT_GOTO_WAYPOINT) &&
559  DistanceManhattan(v->dest_tile, gp.new_tile) <= 3) {
560  /* We got within 3 tiles of our target buoy, so let's skip to our
561  * next order */
562  UpdateVehicleTimetable(v, true);
565  } else {
566  /* Non-buoy orders really need to reach the tile */
567  if (v->dest_tile == gp.new_tile) {
568  if (v->current_order.IsType(OT_GOTO_DEPOT)) {
569  if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) {
571  return;
572  }
573  } else if (v->current_order.IsType(OT_GOTO_STATION)) {
575 
576  /* Process station in the orderlist. */
578  if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations
579  ShipArrivesAt(v, st);
580  v->BeginLoading();
581  } else { // leave stations without docks right aways
584  }
585  }
586  }
587  }
588  }
589  }
590  } else {
591  /* New tile */
592  if (!IsValidTile(gp.new_tile)) goto reverse_direction;
593 
594  dir = ShipGetNewDirectionFromTiles(gp.new_tile, gp.old_tile);
595  assert(dir == DIR_NE || dir == DIR_SE || dir == DIR_SW || dir == DIR_NW);
596  DiagDirection diagdir = DirToDiagDir(dir);
597  tracks = GetAvailShipTracks(gp.new_tile, diagdir);
598  if (tracks == TRACK_BIT_NONE) goto reverse_direction;
599 
600  /* Choose a direction, and continue if we find one */
601  track = ChooseShipTrack(v, gp.new_tile, diagdir, tracks);
602  if (track == INVALID_TRACK) goto reverse_direction;
603 
604  b = _ship_subcoord[diagdir][track];
605 
606  gp.x = (gp.x & ~0xF) | b[0];
607  gp.y = (gp.y & ~0xF) | b[1];
608 
609  /* Call the landscape function and tell it that the vehicle entered the tile */
610  r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
611  if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;
612 
613  if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
614  v->tile = gp.new_tile;
615  v->state = TrackToTrackBits(track);
616 
617  /* Update ship cache when the water class changes. Aqueducts are always canals. */
620  if (old_wc != new_wc) v->UpdateCache();
621  }
622 
623  v->direction = (Direction)b[2];
624  }
625  } else {
626  /* On a bridge */
628  v->x_pos = gp.x;
629  v->y_pos = gp.y;
630  v->UpdatePosition();
631  if ((v->vehstatus & VS_HIDDEN) == 0) v->Vehicle::UpdateViewport(true);
632  return;
633  }
634  }
635 
636  /* update image of ship, as well as delta XY */
637  dir = ShipGetNewDirection(v, gp.x, gp.y);
638  v->x_pos = gp.x;
639  v->y_pos = gp.y;
640  v->z_pos = GetSlopePixelZ(gp.x, gp.y);
641 
642 getout:
643  v->UpdatePosition();
644  v->UpdateViewport(true, true);
645  return;
646 
647 reverse_direction:
648  dir = ReverseDir(v->direction);
649  v->direction = dir;
650  goto getout;
651 }
652 
654 {
655  if (!(this->vehstatus & VS_STOPPED)) this->running_ticks++;
656 
657  ShipController(this);
658 
659  return true;
660 }
661 
671 CommandCost CmdBuildShip(TileIndex tile, DoCommandFlag flags, const Engine *e, uint16 data, Vehicle **ret)
672 {
673  tile = GetShipDepotNorthTile(tile);
674  if (flags & DC_EXEC) {
675  int x;
676  int y;
677 
678  const ShipVehicleInfo *svi = &e->u.ship;
679 
680  Ship *v = new Ship();
681  *ret = v;
682 
683  v->owner = _current_company;
684  v->tile = tile;
685  x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2;
686  y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2;
687  v->x_pos = x;
688  v->y_pos = y;
689  v->z_pos = GetSlopePixelZ(x, y);
690 
691  v->UpdateDeltaXY(v->direction);
693 
694  v->spritenum = svi->image_index;
696  v->cargo_cap = svi->capacity;
697  v->refit_cap = 0;
698 
699  v->last_station_visited = INVALID_STATION;
700  v->last_loading_station = INVALID_STATION;
701  v->engine_type = e->index;
702 
703  v->reliability = e->reliability;
705  v->max_age = e->GetLifeLengthInDays();
706  _new_vehicle_id = v->index;
707 
708  v->state = TRACK_BIT_DEPOT;
709 
710  v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_ships);
712  v->build_year = _cur_year;
713  v->cur_image = SPR_IMG_QUERY;
715 
716  v->UpdateCache();
717 
719  v->SetServiceIntervalIsPercent(Company::Get(_current_company)->settings.vehicle.servint_ispercent);
720 
722 
723  v->cargo_cap = e->DetermineCapacity(v);
724 
726 
727  v->UpdatePosition();
728  }
729 
730  return CommandCost();
731 }
732 
733 bool Ship::FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse)
734 {
735  const Depot *depot = FindClosestShipDepot(this, 0);
736 
737  if (depot == NULL) return false;
738 
739  if (location != NULL) *location = depot->xy;
740  if (destination != NULL) *destination = depot->index;
741 
742  return true;
743 }