54
48
return TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_WATER, 0));
51
static SpriteID GetShipIcon(EngineID engine)
53
uint8 spritenum = ShipVehInfo(engine)->image_index;
55
if (is_custom_sprite(spritenum)) {
56
SpriteID sprite = GetCustomVehicleIcon(engine, DIR_W);
57
if (sprite != 0) return sprite;
59
spritenum = GetEngine(engine)->image_index;
62
return 6 + _ship_sprites[spritenum];
57
65
void DrawShipEngine(int x, int y, EngineID engine, SpriteID pal)
59
int spritenum = ShipVehInfo(engine)->image_index;
61
if (is_custom_sprite(spritenum)) {
62
int sprite = GetCustomVehicleIcon(engine, DIR_W);
65
DrawSprite(sprite, pal, x, y);
68
spritenum = _orig_ship_vehicle_info[engine - SHIP_ENGINES_INDEX].image_index;
70
DrawSprite(6 + _ship_sprites[spritenum], pal, x, y);
67
DrawSprite(GetShipIcon(engine), pal, x, y);
73
70
/** Get the size of the sprite of a ship sprite heading west (used for lists)
78
75
void GetShipSpriteSize(EngineID engine, uint &width, uint &height)
80
SpriteID spritenum = ShipVehInfo(engine)->image_index;
81
SpriteID custom_sprite = 0;
83
if (is_custom_sprite(spritenum)) {
84
custom_sprite = GetCustomVehicleIcon(engine, DIR_W);
85
spritenum = _orig_ship_vehicle_info[engine - SHIP_ENGINES_INDEX].image_index;
87
if (custom_sprite == 0) {
88
spritenum = 6 + _ship_sprites[spritenum];
90
spritenum = custom_sprite;
93
const Sprite *spr = GetSprite(spritenum);
77
const Sprite *spr = GetSprite(GetShipIcon(engine), ST_NORMAL);
95
79
width = spr->width;
96
80
height = spr->height;
99
int Ship::GetImage(Direction direction) const
83
SpriteID Ship::GetImage(Direction direction) const
101
int spritenum = this->spritenum;
85
uint8 spritenum = this->spritenum;
103
87
if (is_custom_sprite(spritenum)) {
104
int sprite = GetCustomVehicleSprite(this, direction);
88
SpriteID sprite = GetCustomVehicleSprite(this, direction);
106
89
if (sprite != 0) return sprite;
107
spritenum = _orig_ship_vehicle_info[this->engine_type - SHIP_ENGINES_INDEX].image_index;
91
spritenum = GetEngine(this->engine_type)->image_index;
109
94
return _ship_sprites[spritenum] + direction;
112
static const Depot* FindClosestShipDepot(const Vehicle* v)
97
static const Depot *FindClosestShipDepot(const Vehicle *v)
114
if (_patches.pathfinder_for_ships == VPF_NPF) { /* NPF is used */
99
if (_settings_game.pf.pathfinder_for_ships == VPF_NPF) { // NPF is used
115
100
Trackdir trackdir = GetVehicleTrackdir(v);
116
101
NPFFoundTargetData ftd = NPFRouteToDepotTrialError(v->tile, trackdir, false, TRANSPORT_WATER, 0, v->owner, INVALID_RAILTYPES);
118
if (ftd.best_bird_dist == 0) return GetDepotByTile(ftd.node.tile); /* Found target */
103
if (ftd.best_bird_dist == 0) return GetDepotByTile(ftd.node.tile); // Found target
120
return NULL; /* Did not find target */
105
return NULL; // Did not find target
123
108
/* OPF or YAPF - find the closest depot */
126
const Depot* best_depot = NULL;
111
const Depot *best_depot = NULL;
127
112
uint best_dist = UINT_MAX;
129
114
FOR_ALL_DEPOTS(depot) {
130
115
TileIndex tile = depot->xy;
131
if (IsTileDepotType(tile, TRANSPORT_WATER) && IsTileOwner(tile, v->owner)) {
116
if (IsShipDepotTile(tile) && IsTileOwner(tile, v->owner)) {
132
117
uint dist = DistanceManhattan(tile, v->tile);
133
118
if (dist < best_dist) {
134
119
best_dist = dist;
240
227
PlayShipSound(this);
243
static void ProcessShipOrder(Vehicle *v)
230
TileIndex Ship::GetOrderStationLocation(StationID station)
247
switch (v->current_order.type) {
249
if (!(v->current_order.flags & OFB_PART_OF_ORDERS)) return;
250
if (v->current_order.flags & OFB_SERVICE_IF_NEEDED &&
251
!v->NeedsServicing()) {
252
UpdateVehicleTimetable(v, true);
253
v->cur_order_index++;
258
case OT_LEAVESTATION:
264
if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0;
266
order = GetVehicleOrder(v, v->cur_order_index);
269
v->current_order.Free();
274
if (order->type == v->current_order.type &&
275
order->flags == v->current_order.flags &&
276
order->dest == v->current_order.dest &&
277
(order->type != OT_GOTO_STATION || GetStation(order->dest)->dock_tile != 0))
280
v->current_order = *order;
282
if (order->type == OT_GOTO_STATION) {
285
if (order->dest == v->last_station_visited)
286
v->last_station_visited = INVALID_STATION;
288
st = GetStation(order->dest);
289
if (st->dock_tile != 0) {
290
v->dest_tile = TILE_ADD(st->dock_tile, ToTileIndexDiff(GetDockOffset(st->dock_tile)));
292
v->cur_order_index++;
294
} else if (order->type == OT_GOTO_DEPOT) {
295
v->dest_tile = GetDepot(order->dest)->xy;
232
if (station == this->last_station_visited) this->last_station_visited = INVALID_STATION;
234
const Station *st = GetStation(station);
235
if (st->dock_tile != INVALID_TILE) {
236
return TILE_ADD(st->dock_tile, ToTileIndexDiff(GetDockOffset(st->dock_tile)));
238
this->cur_order_index++;
300
InvalidateVehicleOrder(v);
302
InvalidateWindowClasses(WC_SHIPS_LIST);
305
243
void Ship::UpdateDeltaXY(Direction direction)
646
574
HandleBrokenShip(v);
649
if (v->current_order.type != OT_LOADING) v->breakdown_ctr--;
577
if (!v->current_order.IsType(OT_LOADING)) v->breakdown_ctr--;
652
580
if (v->vehstatus & VS_STOPPED) return;
655
583
v->HandleLoading();
657
if (v->current_order.type == OT_LOADING) return;
585
if (v->current_order.IsType(OT_LOADING)) return;
659
587
CheckShipLeaveDepot(v);
661
589
if (!ShipAccelerate(v)) return;
665
591
GetNewVehiclePosResult gp = GetNewVehiclePos(v);
666
if (gp.old_tile == gp.new_tile) {
667
/* Staying in tile */
668
if (v->IsInDepot()) {
672
/* Not inside depot */
673
r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
674
if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;
676
/* A leave station order only needs one tick to get processed, so we can
677
* always skip ahead. */
678
if (v->current_order.type == OT_LEAVESTATION) {
679
v->current_order.Free();
680
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
681
} else if (v->dest_tile != 0) {
682
/* We have a target, let's see if we reached it... */
683
if (v->current_order.type == OT_GOTO_STATION &&
684
IsBuoyTile(v->dest_tile) &&
685
DistanceManhattan(v->dest_tile, gp.new_tile) <= 3) {
686
/* We got within 3 tiles of our target buoy, so let's skip to our
688
UpdateVehicleTimetable(v, true);
689
v->cur_order_index++;
690
v->current_order.type = OT_DUMMY;
691
InvalidateVehicleOrder(v);
693
/* Non-buoy orders really need to reach the tile */
694
if (v->dest_tile == gp.new_tile) {
695
if (v->current_order.type == OT_GOTO_DEPOT) {
696
if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) {
697
VehicleEnterDepot(v);
700
} else if (v->current_order.type == OT_GOTO_STATION) {
703
v->last_station_visited = v->current_order.dest;
705
/* Process station in the orderlist. */
706
st = GetStation(v->current_order.dest);
707
if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations
708
ShipArrivesAt(v, st);
710
} else { // leave stations without docks right aways
711
v->current_order.type = OT_LEAVESTATION;
712
v->cur_order_index++;
713
InvalidateVehicleOrder(v);
592
if (v->u.ship.state != TRACK_BIT_WORMHOLE) {
593
/* Not on a bridge */
594
if (gp.old_tile == gp.new_tile) {
595
/* Staying in tile */
596
if (v->IsInDepot()) {
600
/* Not inside depot */
601
r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
602
if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;
604
/* A leave station order only needs one tick to get processed, so we can
605
* always skip ahead. */
606
if (v->current_order.IsType(OT_LEAVESTATION)) {
607
v->current_order.Free();
608
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
609
} else if (v->dest_tile != 0) {
610
/* We have a target, let's see if we reached it... */
611
if (v->current_order.IsType(OT_GOTO_STATION) &&
612
GetStation(v->current_order.GetDestination())->IsBuoy() &&
613
DistanceManhattan(v->dest_tile, gp.new_tile) <= 3) {
614
/* We got within 3 tiles of our target buoy, so let's skip to our
616
UpdateVehicleTimetable(v, true);
617
v->cur_order_index++;
618
v->current_order.MakeDummy();
619
InvalidateVehicleOrder(v, 0);
621
/* Non-buoy orders really need to reach the tile */
622
if (v->dest_tile == gp.new_tile) {
623
if (v->current_order.IsType(OT_GOTO_DEPOT)) {
624
if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) {
625
VehicleEnterDepot(v);
628
} else if (v->current_order.IsType(OT_GOTO_STATION)) {
629
v->last_station_visited = v->current_order.GetDestination();
631
/* Process station in the orderlist. */
632
Station *st = GetStation(v->current_order.GetDestination());
633
if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations
634
ShipArrivesAt(v, st);
636
} else { // leave stations without docks right aways
637
v->current_order.MakeLeaveStation();
638
v->cur_order_index++;
639
InvalidateVehicleOrder(v, 0);
647
DiagDirection diagdir;
649
if (TileX(gp.new_tile) >= MapMaxX() || TileY(gp.new_tile) >= MapMaxY()) {
650
goto reverse_direction;
653
dir = ShipGetNewDirectionFromTiles(gp.new_tile, gp.old_tile);
654
assert(dir == DIR_NE || dir == DIR_SE || dir == DIR_SW || dir == DIR_NW);
655
diagdir = DirToDiagDir(dir);
656
tracks = GetAvailShipTracks(gp.new_tile, diagdir);
657
if (tracks == TRACK_BIT_NONE) goto reverse_direction;
659
/* Choose a direction, and continue if we find one */
660
track = ChooseShipTrack(v, gp.new_tile, diagdir, tracks);
661
if (track == INVALID_TRACK) goto reverse_direction;
663
b = _ship_subcoord[diagdir][track];
665
gp.x = (gp.x & ~0xF) | b[0];
666
gp.y = (gp.y & ~0xF) | b[1];
668
/* Call the landscape function and tell it that the vehicle entered the tile */
669
r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
670
if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;
672
if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
673
v->tile = gp.new_tile;
674
v->u.ship.state = TrackToTrackBits(track);
677
v->direction = (Direction)b[2];
721
DiagDirection diagdir;
723
if (TileX(gp.new_tile) >= MapMaxX() || TileY(gp.new_tile) >= MapMaxY()) {
724
goto reverse_direction;
727
dir = ShipGetNewDirectionFromTiles(gp.new_tile, gp.old_tile);
728
assert(dir == DIR_NE || dir == DIR_SE || dir == DIR_SW || dir == DIR_NW);
729
diagdir = DirToDiagDir(dir);
730
tracks = GetAvailShipTracks(gp.new_tile, diagdir);
731
if (tracks == TRACK_BIT_NONE) goto reverse_direction;
733
/* Choose a direction, and continue if we find one */
734
track = ChooseShipTrack(v, gp.new_tile, diagdir, tracks);
735
if (track == INVALID_TRACK) goto reverse_direction;
737
b = _ship_subcoord[diagdir][track];
739
gp.x = (gp.x & ~0xF) | b[0];
740
gp.y = (gp.y & ~0xF) | b[1];
742
/* Call the landscape function and tell it that the vehicle entered the tile */
743
r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
744
if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction;
746
if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
747
v->tile = gp.new_tile;
748
v->u.ship.state = TrackToTrackBits(track);
751
v->direction = (Direction)b[2];
681
if (!IsTileType(gp.new_tile, MP_TUNNELBRIDGE) || !HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
684
VehicleMove(v, !(v->vehstatus & VS_HIDDEN));
754
689
/* update image of ship, as well as delta XY */
784
718
ShipController(this);
788
void ShipsYearlyLoop()
792
FOR_ALL_VEHICLES(v) {
793
if (v->type == VEH_SHIP) {
794
v->profit_last_year = v->profit_this_year;
795
v->profit_this_year = 0;
796
InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
801
721
/** Build a ship.
802
722
* @param tile tile of depot where ship is built
803
723
* @param flags type of operation
804
724
* @param p1 ship type being built (engine)
805
725
* @param p2 unused
807
CommandCost CmdBuildShip(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
727
CommandCost CmdBuildShip(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
813
if (!IsEngineBuildable(p1, VEH_SHIP, _current_player)) return_cmd_error(STR_SHIP_NOT_AVAILABLE);
815
value = EstimateShipCost(p1);
731
if (!IsEngineBuildable(p1, VEH_SHIP, _current_company)) return_cmd_error(STR_SHIP_NOT_AVAILABLE);
733
const Engine *e = GetEngine(p1);
734
CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost());
736
/* Engines without valid cargo should not be available */
737
if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR;
816
739
if (flags & DC_QUERY_COST) return value;
818
741
/* The ai_new queries the vehicle cost before building the route,
819
742
* so we must check against cheaters no sooner than now. --pasky */
820
if (!IsTileDepotType(tile, TRANSPORT_WATER)) return CMD_ERROR;
821
if (!IsTileOwner(tile, _current_player)) return CMD_ERROR;
743
if (!IsShipDepotTile(tile)) return CMD_ERROR;
744
if (!IsTileOwner(tile, _current_company)) return CMD_ERROR;
823
746
unit_num = (flags & DC_AUTOREPLACE) ? 0 : GetFreeUnitNumber(VEH_SHIP);
825
if (!Vehicle::AllocateList(NULL, 1) || unit_num > _patches.max_ships)
748
if (!Vehicle::CanAllocateItem() || unit_num > _settings_game.vehicle.max_ships)
826
749
return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
828
751
if (flags & DC_EXEC) {
916
838
CommandCost ret(EXPENSES_NEW_VEHICLES, -v->value);
918
840
if (flags & DC_EXEC) {
919
InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
920
RebuildVehicleLists();
921
InvalidateWindow(WC_COMPANY, v->owner);
922
DeleteWindowById(WC_VEHICLE_VIEW, v->index);
923
DeleteDepotHighlightOfVehicle(v);
930
/** Start/Stop a ship.
932
* @param flags type of operation
933
* @param p1 ship ID to start/stop
936
CommandCost CmdStartStopShip(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
847
bool Ship::FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse)
938
if (!IsValidVehicleID(p1)) return CMD_ERROR;
940
Vehicle *v = GetVehicle(p1);
942
if (v->type != VEH_SHIP || !CheckOwnership(v->owner)) return CMD_ERROR;
944
/* Check if this ship can be started/stopped. The callback will fail or
945
* return 0xFF if it can. */
946
uint16 callback = GetVehicleCallback(CBID_VEHICLE_START_STOP_CHECK, 0, 0, v->engine_type, v);
947
if (callback != CALLBACK_FAILED && GB(callback, 0, 8) != 0xFF) {
948
StringID error = GetGRFStringID(GetEngineGRFID(v->engine_type), 0xD000 + callback);
949
return_cmd_error(error);
952
if (flags & DC_EXEC) {
953
if (v->IsStoppedInDepot()) {
954
DeleteVehicleNews(p1, STR_981C_SHIP_IS_WAITING_IN_DEPOT);
957
v->vehstatus ^= VS_STOPPED;
959
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
960
InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
961
InvalidateWindowClasses(WC_SHIPS_LIST);
964
return CommandCost();
849
const Depot *depot = FindClosestShipDepot(this);
851
if (depot == NULL) return false;
853
if (location != NULL) *location = depot->xy;
854
if (destination != NULL) *destination = depot->index;
967
859
/** Send a ship to the depot.
972
864
* - p2 bit 0-3 - DEPOT_ flags (see vehicle.h)
973
865
* - p2 bit 8-10 - VLW flag (for mass goto depot)
975
CommandCost CmdSendShipToDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
867
CommandCost CmdSendShipToDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
980
869
if (p2 & DEPOT_MASS_SEND) {
981
870
/* Mass goto depot requested */
982
871
if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR;
983
return SendAllVehiclesToDepot(VEH_SHIP, flags, p2 & DEPOT_SERVICE, _current_player, (p2 & VLW_MASK), p1);
872
return SendAllVehiclesToDepot(VEH_SHIP, flags, p2 & DEPOT_SERVICE, _current_company, (p2 & VLW_MASK), p1);
986
875
if (!IsValidVehicleID(p1)) return CMD_ERROR;
990
if (v->type != VEH_SHIP || !CheckOwnership(v->owner)) return CMD_ERROR;
992
if (v->vehstatus & VS_CRASHED) return CMD_ERROR;
994
if (v->IsInDepot()) return CMD_ERROR;
996
/* If the current orders are already goto-depot */
997
if (v->current_order.type == OT_GOTO_DEPOT) {
998
if (!!(p2 & DEPOT_SERVICE) == HasBit(v->current_order.flags, OF_HALT_IN_DEPOT)) {
999
/* We called with a different DEPOT_SERVICE setting.
1000
* Now we change the setting to apply the new one and let the vehicle head for the same depot.
1001
* Note: the if is (true for requesting service == true for ordered to stop in depot) */
1002
if (flags & DC_EXEC) {
1003
ClrBit(v->current_order.flags, OF_PART_OF_ORDERS);
1004
ToggleBit(v->current_order.flags, OF_HALT_IN_DEPOT);
1005
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
1007
return CommandCost();
1010
if (p2 & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders
1011
if (flags & DC_EXEC) {
1012
/* If the orders to 'goto depot' are in the orders list (forced servicing),
1013
* then skip to the next order; effectively cancelling this forced service */
1014
if (HasBit(v->current_order.flags, OF_PART_OF_ORDERS))
1015
v->cur_order_index++;
1017
v->current_order.type = OT_DUMMY;
1018
v->current_order.flags = 0;
1019
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
1021
return CommandCost();
1024
dep = FindClosestShipDepot(v);
1025
if (dep == NULL) return_cmd_error(STR_981A_UNABLE_TO_FIND_LOCAL_DEPOT);
1027
if (flags & DC_EXEC) {
1028
if (v->current_order.type == OT_LOADING) v->LeaveStation();
1030
v->dest_tile = dep->xy;
1031
v->current_order.type = OT_GOTO_DEPOT;
1032
v->current_order.flags = OFB_NON_STOP;
1033
if (!(p2 & DEPOT_SERVICE)) SetBit(v->current_order.flags, OF_HALT_IN_DEPOT);
1034
v->current_order.refit_cargo = CT_INVALID;
1035
v->current_order.dest = dep->index;
1036
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
1039
return CommandCost();
877
Vehicle *v = GetVehicle(p1);
879
if (v->type != VEH_SHIP) return CMD_ERROR;
881
return v->SendToDepot(flags, (DepotCommand)(p2 & DEPOT_COMMAND_MASK));