109 static void TrainCargoChanged(Vehicle* v) |
110 static void TrainCargoChanged(Vehicle* v) |
110 { |
111 { |
111 uint32 weight = 0; |
112 uint32 weight = 0; |
112 |
113 |
113 for (Vehicle *u = v; u != NULL; u = u->next) { |
114 for (Vehicle *u = v; u != NULL; u = u->next) { |
114 uint32 vweight = GetCargo(u->cargo_type)->weight * u->cargo_count * FreightWagonMult(u->cargo_type) / 16; |
115 uint32 vweight = GetCargo(u->cargo_type)->weight * u->cargo.Count() * FreightWagonMult(u->cargo_type) / 16; |
115 |
116 |
116 /* Vehicle weight is not added for articulated parts. */ |
117 /* Vehicle weight is not added for articulated parts. */ |
117 if (!IsArticulatedPart(u)) { |
118 if (!IsArticulatedPart(u)) { |
118 /* vehicle weight is the sum of the weight of the vehicle and the weight of its cargo */ |
119 /* vehicle weight is the sum of the weight of the vehicle and the weight of its cargo */ |
119 vweight += GetVehicleProperty(u, 0x16, RailVehInfo(u->engine_type)->weight); |
120 vweight += GetVehicleProperty(u, 0x16, RailVehInfo(u->engine_type)->weight); |
460 img = orig_rail_vehicle_info[v->engine_type].image_index; |
461 img = orig_rail_vehicle_info[v->engine_type].image_index; |
461 } |
462 } |
462 |
463 |
463 base = _engine_sprite_base[img] + ((direction + _engine_sprite_add[img]) & _engine_sprite_and[img]); |
464 base = _engine_sprite_base[img] + ((direction + _engine_sprite_add[img]) & _engine_sprite_and[img]); |
464 |
465 |
465 if (v->cargo_count >= v->cargo_cap / 2) base += _wagon_full_adder[img]; |
466 if (v->cargo.Count() >= v->cargo_cap / 2U) base += _wagon_full_adder[img]; |
466 return base; |
467 return base; |
467 } |
468 } |
468 |
469 |
469 void DrawTrainEngine(int x, int y, EngineID engine, SpriteID pal) |
470 void DrawTrainEngine(int x, int y, EngineID engine, SpriteID pal) |
470 { |
471 { |
500 } |
501 } |
501 } |
502 } |
502 DrawSprite(image, pal, x, y); |
503 DrawSprite(image, pal, x, y); |
503 } |
504 } |
504 |
505 |
505 static int32 CmdBuildRailWagon(EngineID engine, TileIndex tile, uint32 flags) |
506 static CommandCost CmdBuildRailWagon(EngineID engine, TileIndex tile, uint32 flags) |
506 { |
507 { |
507 SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); |
508 SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES); |
508 |
509 |
509 const RailVehicleInfo *rvi = RailVehInfo(engine); |
510 const RailVehicleInfo *rvi = RailVehInfo(engine); |
510 int32 value = (GetEngineProperty(engine, 0x17, rvi->base_cost) * _price.build_railwagon) >> 8; |
511 CommandCost value((GetEngineProperty(engine, 0x17, rvi->base_cost) * _price.build_railwagon) >> 8); |
511 |
512 |
512 uint num_vehicles = 1 + CountArticulatedParts(engine); |
513 uint num_vehicles = 1 + CountArticulatedParts(engine); |
513 |
514 |
514 if (!(flags & DC_QUERY_COST)) { |
515 if (!(flags & DC_QUERY_COST)) { |
515 Vehicle *vl[11]; // Allow for wagon and upto 10 artic parts. |
516 Vehicle *vl[11]; // Allow for wagon and upto 10 artic parts. |
563 } |
564 } |
564 |
565 |
565 v->cargo_type = rvi->cargo_type; |
566 v->cargo_type = rvi->cargo_type; |
566 v->cargo_subtype = 0; |
567 v->cargo_subtype = 0; |
567 v->cargo_cap = rvi->capacity; |
568 v->cargo_cap = rvi->capacity; |
568 v->value = value; |
569 v->value = value.GetCost(); |
569 // v->day_counter = 0; |
570 // v->day_counter = 0; |
570 |
571 |
571 v->u.rail.railtype = rvi->railtype; |
572 v->u.rail.railtype = rvi->railtype; |
572 |
573 |
573 v->build_year = _cur_year; |
574 v->build_year = _cur_year; |
609 break; |
610 break; |
610 } |
611 } |
611 } |
612 } |
612 } |
613 } |
613 |
614 |
614 static int32 EstimateTrainCost(EngineID engine, const RailVehicleInfo* rvi) |
615 static CommandCost EstimateTrainCost(EngineID engine, const RailVehicleInfo* rvi) |
615 { |
616 { |
616 return GetEngineProperty(engine, 0x17, rvi->base_cost) * (_price.build_railvehicle >> 3) >> 5; |
617 return CommandCost(GetEngineProperty(engine, 0x17, rvi->base_cost) * (_price.build_railvehicle >> 3) >> 5); |
617 } |
618 } |
618 |
619 |
619 static void AddRearEngineToMultiheadedTrain(Vehicle* v, Vehicle* u, bool building) |
620 static void AddRearEngineToMultiheadedTrain(Vehicle* v, Vehicle* u, bool building) |
620 { |
621 { |
621 u->direction = v->direction; |
622 u->direction = v->direction; |
649 * @param flags type of operation |
650 * @param flags type of operation |
650 * @param p1 engine type id |
651 * @param p1 engine type id |
651 * @param p2 bit 0 when set, the train will get number 0, otherwise it will get a free number |
652 * @param p2 bit 0 when set, the train will get number 0, otherwise it will get a free number |
652 * bit 1 prevents any free cars from being added to the train |
653 * bit 1 prevents any free cars from being added to the train |
653 */ |
654 */ |
654 int32 CmdBuildRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
655 CommandCost CmdBuildRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
655 { |
656 { |
656 /* Check if the engine-type is valid (for the player) */ |
657 /* Check if the engine-type is valid (for the player) */ |
657 if (!IsEngineBuildable(p1, VEH_TRAIN, _current_player)) return_cmd_error(STR_RAIL_VEHICLE_NOT_AVAILABLE); |
658 if (!IsEngineBuildable(p1, VEH_TRAIN, _current_player)) return_cmd_error(STR_RAIL_VEHICLE_NOT_AVAILABLE); |
658 |
659 |
659 /* Check if the train is actually being built in a depot belonging |
660 /* Check if the train is actually being built in a depot belonging |
671 /* We need to see if the engine got power on the tile to avoid eletric engines in non-electric depots */ |
672 /* We need to see if the engine got power on the tile to avoid eletric engines in non-electric depots */ |
672 if (!HasPowerOnRail(rvi->railtype, GetRailType(tile))) return CMD_ERROR; |
673 if (!HasPowerOnRail(rvi->railtype, GetRailType(tile))) return CMD_ERROR; |
673 |
674 |
674 if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildRailWagon(p1, tile, flags); |
675 if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildRailWagon(p1, tile, flags); |
675 |
676 |
676 int32 value = EstimateTrainCost(p1, rvi); |
677 CommandCost value = EstimateTrainCost(p1, rvi); |
677 |
678 |
678 uint num_vehicles = |
679 uint num_vehicles = |
679 (rvi->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1) + |
680 (rvi->railveh_type == RAILVEH_MULTIHEAD ? 2 : 1) + |
680 CountArticulatedParts(p1); |
681 CountArticulatedParts(p1); |
681 |
682 |
710 v->spritenum = rvi->image_index; |
711 v->spritenum = rvi->image_index; |
711 v->cargo_type = rvi->cargo_type; |
712 v->cargo_type = rvi->cargo_type; |
712 v->cargo_subtype = 0; |
713 v->cargo_subtype = 0; |
713 v->cargo_cap = rvi->capacity; |
714 v->cargo_cap = rvi->capacity; |
714 v->max_speed = rvi->max_speed; |
715 v->max_speed = rvi->max_speed; |
715 v->value = value; |
716 v->value = value.GetCost(); |
716 v->last_station_visited = INVALID_STATION; |
717 v->last_station_visited = INVALID_STATION; |
717 v->dest_tile = 0; |
718 v->dest_tile = 0; |
718 |
719 |
719 v->engine_type = p1; |
720 v->engine_type = p1; |
720 |
721 |
907 * @param p1 various bitstuffed elements |
908 * @param p1 various bitstuffed elements |
908 * - p1 (bit 0 - 15) source vehicle index |
909 * - p1 (bit 0 - 15) source vehicle index |
909 * - p1 (bit 16 - 31) what wagon to put the source wagon AFTER, XXX - INVALID_VEHICLE to make a new line |
910 * - p1 (bit 16 - 31) what wagon to put the source wagon AFTER, XXX - INVALID_VEHICLE to make a new line |
910 * @param p2 (bit 0) move all vehicles following the source vehicle |
911 * @param p2 (bit 0) move all vehicles following the source vehicle |
911 */ |
912 */ |
912 int32 CmdMoveRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
913 CommandCost CmdMoveRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
913 { |
914 { |
914 VehicleID s = GB(p1, 0, 16); |
915 VehicleID s = GB(p1, 0, 16); |
915 VehicleID d = GB(p1, 16, 16); |
916 VehicleID d = GB(p1, 16, 16); |
916 |
917 |
917 if (!IsValidVehicleID(s)) return CMD_ERROR; |
918 if (!IsValidVehicleID(s)) return CMD_ERROR; |
952 } |
953 } |
953 |
954 |
954 if (IsMultiheaded(src) && !IsTrainEngine(src)) return_cmd_error(STR_REAR_ENGINE_FOLLOW_FRONT_ERROR); |
955 if (IsMultiheaded(src) && !IsTrainEngine(src)) return_cmd_error(STR_REAR_ENGINE_FOLLOW_FRONT_ERROR); |
955 |
956 |
956 /* when moving all wagons, we can't have the same src_head and dst_head */ |
957 /* when moving all wagons, we can't have the same src_head and dst_head */ |
957 if (HASBIT(p2, 0) && src_head == dst_head) return 0; |
958 if (HASBIT(p2, 0) && src_head == dst_head) return CommandCost(); |
958 |
959 |
959 { |
960 { |
960 int max_len = _patches.mammoth_trains ? 100 : 9; |
961 int max_len = _patches.mammoth_trains ? 100 : 9; |
961 |
962 |
962 /* check if all vehicles in the source train are stopped inside a depot. */ |
963 /* check if all vehicles in the source train are stopped inside a depot. */ |
1151 } |
1152 } |
1152 |
1153 |
1153 RebuildVehicleLists(); |
1154 RebuildVehicleLists(); |
1154 } |
1155 } |
1155 |
1156 |
1156 return 0; |
1157 return CommandCost(); |
1157 } |
1158 } |
1158 |
1159 |
1159 /** Start/Stop a train. |
1160 /** Start/Stop a train. |
1160 * @param tile unused |
1161 * @param tile unused |
1161 * @param flags type of operation |
1162 * @param flags type of operation |
1162 * @param p1 train to start/stop |
1163 * @param p1 train to start/stop |
1163 * @param p2 unused |
1164 * @param p2 unused |
1164 */ |
1165 */ |
1165 int32 CmdStartStopTrain(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
1166 CommandCost CmdStartStopTrain(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
1166 { |
1167 { |
1167 if (!IsValidVehicleID(p1)) return CMD_ERROR; |
1168 if (!IsValidVehicleID(p1)) return CMD_ERROR; |
1168 |
1169 |
1169 Vehicle *v = GetVehicle(p1); |
1170 Vehicle *v = GetVehicle(p1); |
1170 |
1171 |
1187 |
1188 |
1188 v->vehstatus ^= VS_STOPPED; |
1189 v->vehstatus ^= VS_STOPPED; |
1189 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
1190 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
1190 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); |
1191 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); |
1191 } |
1192 } |
1192 return 0; |
1193 return CommandCost(); |
1193 } |
1194 } |
1194 |
1195 |
1195 /** Sell a (single) train wagon/engine. |
1196 /** Sell a (single) train wagon/engine. |
1196 * @param tile unused |
1197 * @param tile unused |
1197 * @param flags type of operation |
1198 * @param flags type of operation |
1201 * - p2 = 1: sell the vehicle and all vehicles following it in the chain |
1202 * - p2 = 1: sell the vehicle and all vehicles following it in the chain |
1202 if the wagon is dragged, don't delete the possibly belonging rear-engine to some front |
1203 if the wagon is dragged, don't delete the possibly belonging rear-engine to some front |
1203 * - p2 = 2: when selling attached locos, rearrange all vehicles after it to separate lines; |
1204 * - p2 = 2: when selling attached locos, rearrange all vehicles after it to separate lines; |
1204 * all wagons of the same type will go on the same line. Used by the AI currently |
1205 * all wagons of the same type will go on the same line. Used by the AI currently |
1205 */ |
1206 */ |
1206 int32 CmdSellRailWagon(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
1207 CommandCost CmdSellRailWagon(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
1207 { |
1208 { |
1208 /* Check if we deleted a vehicle window */ |
1209 /* Check if we deleted a vehicle window */ |
1209 Window *w = NULL; |
1210 Window *w = NULL; |
1210 |
1211 |
1211 if (!IsValidVehicleID(p1) || p2 > 2) return CMD_ERROR; |
1212 if (!IsValidVehicleID(p1) || p2 > 2) return CMD_ERROR; |
1245 * rear engine of the loco (from the point of deletion onwards) */ |
1246 * rear engine of the loco (from the point of deletion onwards) */ |
1246 Vehicle *rear = (IsMultiheaded(v) && |
1247 Vehicle *rear = (IsMultiheaded(v) && |
1247 IsTrainEngine(v)) ? v->u.rail.other_multiheaded_part : NULL; |
1248 IsTrainEngine(v)) ? v->u.rail.other_multiheaded_part : NULL; |
1248 |
1249 |
1249 if (rear != NULL) { |
1250 if (rear != NULL) { |
1250 cost -= rear->value; |
1251 cost.AddCost(-rear->value); |
1251 if (flags & DC_EXEC) { |
1252 if (flags & DC_EXEC) { |
1252 UnlinkWagon(rear, first); |
1253 UnlinkWagon(rear, first); |
1253 DeleteDepotHighlightOfVehicle(rear); |
1254 DeleteDepotHighlightOfVehicle(rear); |
1254 DeleteVehicle(rear); |
1255 DeleteVehicle(rear); |
1255 } |
1256 } |
1350 if (IsTrainEngine(v)) { |
1351 if (IsTrainEngine(v)) { |
1351 /* We got a front engine of a multiheaded set. Now we will sell the rear end too */ |
1352 /* We got a front engine of a multiheaded set. Now we will sell the rear end too */ |
1352 Vehicle *rear = v->u.rail.other_multiheaded_part; |
1353 Vehicle *rear = v->u.rail.other_multiheaded_part; |
1353 |
1354 |
1354 if (rear != NULL) { |
1355 if (rear != NULL) { |
1355 cost -= rear->value; |
1356 cost.AddCost(-rear->value); |
1356 |
1357 |
1357 /* If this is a multiheaded vehicle with nothing |
1358 /* If this is a multiheaded vehicle with nothing |
1358 * between the parts, tmp will be pointing to the |
1359 * between the parts, tmp will be pointing to the |
1359 * rear part, which is unlinked from the train and |
1360 * rear part, which is unlinked from the train and |
1360 * deleted here. However, because tmp has already |
1361 * deleted here. However, because tmp has already |
1614 * @param tile unused |
1615 * @param tile unused |
1615 * @param flags type of operation |
1616 * @param flags type of operation |
1616 * @param p1 train to reverse |
1617 * @param p1 train to reverse |
1617 * @param p2 if true, reverse a unit in a train (needs to be in a depot) |
1618 * @param p2 if true, reverse a unit in a train (needs to be in a depot) |
1618 */ |
1619 */ |
1619 int32 CmdReverseTrainDirection(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
1620 CommandCost CmdReverseTrainDirection(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
1620 { |
1621 { |
1621 if (!IsValidVehicleID(p1)) return CMD_ERROR; |
1622 if (!IsValidVehicleID(p1)) return CMD_ERROR; |
1622 |
1623 |
1623 Vehicle *v = GetVehicle(p1); |
1624 Vehicle *v = GetVehicle(p1); |
1624 |
1625 |
1654 SetLastSpeed(v, 0); |
1655 SetLastSpeed(v, 0); |
1655 ReverseTrainDirection(v); |
1656 ReverseTrainDirection(v); |
1656 } |
1657 } |
1657 } |
1658 } |
1658 } |
1659 } |
1659 return 0; |
1660 return CommandCost(); |
1660 } |
1661 } |
1661 |
1662 |
1662 /** Force a train through a red signal |
1663 /** Force a train through a red signal |
1663 * @param tile unused |
1664 * @param tile unused |
1664 * @param flags type of operation |
1665 * @param flags type of operation |
1665 * @param p1 train to ignore the red signal |
1666 * @param p1 train to ignore the red signal |
1666 * @param p2 unused |
1667 * @param p2 unused |
1667 */ |
1668 */ |
1668 int32 CmdForceTrainProceed(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
1669 CommandCost CmdForceTrainProceed(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
1669 { |
1670 { |
1670 if (!IsValidVehicleID(p1)) return CMD_ERROR; |
1671 if (!IsValidVehicleID(p1)) return CMD_ERROR; |
1671 |
1672 |
1672 Vehicle *v = GetVehicle(p1); |
1673 Vehicle *v = GetVehicle(p1); |
1673 |
1674 |
1674 if (v->type != VEH_TRAIN || !CheckOwnership(v->owner)) return CMD_ERROR; |
1675 if (v->type != VEH_TRAIN || !CheckOwnership(v->owner)) return CMD_ERROR; |
1675 |
1676 |
1676 if (flags & DC_EXEC) v->u.rail.force_proceed = 0x50; |
1677 if (flags & DC_EXEC) v->u.rail.force_proceed = 0x50; |
1677 |
1678 |
1678 return 0; |
1679 return CommandCost(); |
1679 } |
1680 } |
1680 |
1681 |
1681 /** Refits a train to the specified cargo type. |
1682 /** Refits a train to the specified cargo type. |
1682 * @param tile unused |
1683 * @param tile unused |
1683 * @param flags type of operation |
1684 * @param flags type of operation |
1686 * - p2 = (bit 0-7) - the new cargo type to refit to |
1687 * - p2 = (bit 0-7) - the new cargo type to refit to |
1687 * - p2 = (bit 8-15) - the new cargo subtype to refit to |
1688 * - p2 = (bit 8-15) - the new cargo subtype to refit to |
1688 * - p2 = (bit 16) - refit only this vehicle |
1689 * - p2 = (bit 16) - refit only this vehicle |
1689 * @return cost of refit or error |
1690 * @return cost of refit or error |
1690 */ |
1691 */ |
1691 int32 CmdRefitRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
1692 CommandCost CmdRefitRailVehicle(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
1692 { |
1693 { |
1693 CargoID new_cid = GB(p2, 0, 8); |
1694 CargoID new_cid = GB(p2, 0, 8); |
1694 byte new_subtype = GB(p2, 8, 8); |
1695 byte new_subtype = GB(p2, 8, 8); |
1695 bool only_this = HASBIT(p2, 16); |
1696 bool only_this = HASBIT(p2, 16); |
1696 |
1697 |
1753 } |
1754 } |
1754 } |
1755 } |
1755 |
1756 |
1756 if (amount != 0) { |
1757 if (amount != 0) { |
1757 if (new_cid != v->cargo_type) { |
1758 if (new_cid != v->cargo_type) { |
1758 cost += GetRefitCost(v->engine_type); |
1759 cost.AddCost(GetRefitCost(v->engine_type)); |
1759 } |
1760 } |
1760 |
1761 |
1761 num += amount; |
1762 num += amount; |
1762 if (flags & DC_EXEC) { |
1763 if (flags & DC_EXEC) { |
1763 v->cargo_count = (v->cargo_type == new_cid) ? min(amount, v->cargo_count) : 0; |
1764 v->cargo.Truncate((v->cargo_type == new_cid) ? amount : 0); |
1764 v->cargo_type = new_cid; |
1765 v->cargo_type = new_cid; |
1765 v->cargo_cap = amount; |
1766 v->cargo_cap = amount; |
1766 v->cargo_subtype = new_subtype; |
1767 v->cargo_subtype = new_subtype; |
1767 InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
1768 InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
1768 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); |
1769 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile); |
1870 * @param p1 train to send to the depot |
1871 * @param p1 train to send to the depot |
1871 * @param p2 various bitmasked elements |
1872 * @param p2 various bitmasked elements |
1872 * - p2 bit 0-3 - DEPOT_ flags (see vehicle.h) |
1873 * - p2 bit 0-3 - DEPOT_ flags (see vehicle.h) |
1873 * - p2 bit 8-10 - VLW flag (for mass goto depot) |
1874 * - p2 bit 8-10 - VLW flag (for mass goto depot) |
1874 */ |
1875 */ |
1875 int32 CmdSendTrainToDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
1876 CommandCost CmdSendTrainToDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) |
1876 { |
1877 { |
1877 if (p2 & DEPOT_MASS_SEND) { |
1878 if (p2 & DEPOT_MASS_SEND) { |
1878 /* Mass goto depot requested */ |
1879 /* Mass goto depot requested */ |
1879 if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR; |
1880 if (!ValidVLWFlags(p2 & VLW_MASK)) return CMD_ERROR; |
1880 return SendAllVehiclesToDepot(VEH_TRAIN, flags, p2 & DEPOT_SERVICE, _current_player, (p2 & VLW_MASK), p1); |
1881 return SendAllVehiclesToDepot(VEH_TRAIN, flags, p2 & DEPOT_SERVICE, _current_player, (p2 & VLW_MASK), p1); |
1895 * Note: the if is (true for requesting service == true for ordered to stop in depot) */ |
1896 * Note: the if is (true for requesting service == true for ordered to stop in depot) */ |
1896 if (flags & DC_EXEC) { |
1897 if (flags & DC_EXEC) { |
1897 TOGGLEBIT(v->current_order.flags, OFB_HALT_IN_DEPOT); |
1898 TOGGLEBIT(v->current_order.flags, OFB_HALT_IN_DEPOT); |
1898 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
1899 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
1899 } |
1900 } |
1900 return 0; |
1901 return CommandCost(); |
1901 } |
1902 } |
1902 |
1903 |
1903 if (p2 & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders |
1904 if (p2 & DEPOT_DONT_CANCEL) return CMD_ERROR; // Requested no cancelation of depot orders |
1904 if (flags & DC_EXEC) { |
1905 if (flags & DC_EXEC) { |
1905 if (HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS)) { |
1906 if (HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS)) { |
1908 |
1909 |
1909 v->current_order.type = OT_DUMMY; |
1910 v->current_order.type = OT_DUMMY; |
1910 v->current_order.flags = 0; |
1911 v->current_order.flags = 0; |
1911 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
1912 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
1912 } |
1913 } |
1913 return 0; |
1914 return CommandCost(); |
1914 } |
1915 } |
1915 |
1916 |
1916 /* check if at a standstill (not stopped only) in a depot |
1917 /* check if at a standstill (not stopped only) in a depot |
1917 * the check is down here to make it possible to alter stop/service for trains entering the depot */ |
1918 * the check is down here to make it possible to alter stop/service for trains entering the depot */ |
1918 if (IsTileDepotType(v->tile, TRANSPORT_RAIL) && v->cur_speed == 0) return CMD_ERROR; |
1919 if (IsTileDepotType(v->tile, TRANSPORT_RAIL) && v->cur_speed == 0) return CMD_ERROR; |
1932 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
1933 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
1933 /* If there is no depot in front, reverse automatically */ |
1934 /* If there is no depot in front, reverse automatically */ |
1934 if (tfdd.reverse) DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION); |
1935 if (tfdd.reverse) DoCommand(v->tile, v->index, 0, DC_EXEC, CMD_REVERSE_TRAIN_DIRECTION); |
1935 } |
1936 } |
1936 |
1937 |
1937 return 0; |
1938 return CommandCost(); |
1938 } |
1939 } |
1939 |
1940 |
1940 |
1941 |
1941 void OnTick_Train() |
1942 void OnTick_Train() |
1942 { |
1943 { |
2391 } |
2393 } |
2392 |
2394 |
2393 /* check if we've reached the waypoint? */ |
2395 /* check if we've reached the waypoint? */ |
2394 bool at_waypoint = false; |
2396 bool at_waypoint = false; |
2395 if (v->current_order.type == OT_GOTO_WAYPOINT && v->tile == v->dest_tile) { |
2397 if (v->current_order.type == OT_GOTO_WAYPOINT && v->tile == v->dest_tile) { |
|
2398 UpdateVehicleTimetable(v, true); |
2396 v->cur_order_index++; |
2399 v->cur_order_index++; |
2397 at_waypoint = true; |
2400 at_waypoint = true; |
2398 } |
2401 } |
2399 |
2402 |
2400 /* check if we've reached a non-stop station while TTDPatch nonstop is enabled.. */ |
2403 /* check if we've reached a non-stop station while TTDPatch nonstop is enabled.. */ |
2401 if (_patches.new_nonstop && |
2404 if (_patches.new_nonstop && |
2402 v->current_order.flags & OF_NON_STOP && |
2405 v->current_order.flags & OF_NON_STOP && |
2403 IsTileType(v->tile, MP_STATION) && |
2406 IsTileType(v->tile, MP_STATION) && |
2404 v->current_order.dest == GetStationIndex(v->tile)) { |
2407 v->current_order.dest == GetStationIndex(v->tile)) { |
|
2408 UpdateVehicleTimetable(v, true); |
2405 v->cur_order_index++; |
2409 v->cur_order_index++; |
2406 } |
2410 } |
2407 |
2411 |
2408 /* Get the current order */ |
2412 /* Get the current order */ |
2409 if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0; |
2413 if (v->cur_order_index >= v->num_orders) v->cur_order_index = 0; |
2523 v->current_order.dest = 0; |
2527 v->current_order.dest = 0; |
2524 } |
2528 } |
2525 |
2529 |
2526 static byte AfterSetTrainPos(Vehicle *v, bool new_tile) |
2530 static byte AfterSetTrainPos(Vehicle *v, bool new_tile) |
2527 { |
2531 { |
2528 /* need this hint so it returns the right z coordinate on bridges. */ |
|
2529 byte new_z = GetSlopeZ(v->x_pos, v->y_pos); |
|
2530 |
|
2531 byte old_z = v->z_pos; |
2532 byte old_z = v->z_pos; |
2532 v->z_pos = new_z; |
2533 v->z_pos = GetSlopeZ(v->x_pos, v->y_pos); |
2533 |
2534 |
2534 if (new_tile) { |
2535 if (new_tile) { |
2535 CLRBIT(v->u.rail.flags, VRF_GOINGUP); |
2536 CLRBIT(v->u.rail.flags, VRF_GOINGUP); |
2536 CLRBIT(v->u.rail.flags, VRF_GOINGDOWN); |
2537 CLRBIT(v->u.rail.flags, VRF_GOINGDOWN); |
2537 |
2538 |
2538 if (new_z != old_z) { |
2539 if (v->u.rail.track == TRACK_BIT_X || v->u.rail.track == TRACK_BIT_Y) { |
2539 TileIndex tile = TileVirtXY(v->x_pos, v->y_pos); |
2540 /* Any track that isn't TRACK_BIT_X or TRACK_BIT_Y cannot be sloped. |
2540 |
2541 * To check whether the current tile is sloped, and in which |
2541 /* XXX workaround, whole UP/DOWN detection needs overhaul */ |
2542 * direction it is sloped, we get the 'z' at the center of |
2542 if (!IsTunnelTile(tile)) { |
2543 * the tile (middle_z) and the edge of the tile (old_z), |
2543 SETBIT(v->u.rail.flags, (new_z > old_z) ? VRF_GOINGUP : VRF_GOINGDOWN); |
2544 * which we then can compare. */ |
|
2545 static const int HALF_TILE_SIZE = TILE_SIZE / 2; |
|
2546 static const int INV_TILE_SIZE_MASK = ~(TILE_SIZE - 1); |
|
2547 |
|
2548 byte middle_z = GetSlopeZ((v->x_pos & INV_TILE_SIZE_MASK) | HALF_TILE_SIZE, (v->y_pos & INV_TILE_SIZE_MASK) | HALF_TILE_SIZE); |
|
2549 |
|
2550 /* For some reason tunnel tiles are always given as sloped :( |
|
2551 * But they are not sloped... */ |
|
2552 if (middle_z != v->z_pos && !IsTunnelTile(TileVirtXY(v->x_pos, v->y_pos))) { |
|
2553 SETBIT(v->u.rail.flags, (middle_z > old_z) ? VRF_GOINGUP : VRF_GOINGDOWN); |
2544 } |
2554 } |
2545 } |
2555 } |
2546 } |
2556 } |
2547 |
2557 |
2548 VehiclePositionChanged(v); |
2558 VehiclePositionChanged(v); |
2657 UpdateSignalsOnSegment(tile, _otherside_signal_directions[i]); |
2667 UpdateSignalsOnSegment(tile, _otherside_signal_directions[i]); |
2658 } |
2668 } |
2659 } |
2669 } |
2660 |
2670 |
2661 |
2671 |
|
2672 static void SetVehicleCrashed(Vehicle *v) |
|
2673 { |
|
2674 if (v->u.rail.crash_anim_pos != 0) return; |
|
2675 |
|
2676 v->u.rail.crash_anim_pos++; |
|
2677 |
|
2678 Vehicle *u = v; |
|
2679 BEGIN_ENUM_WAGONS(v) |
|
2680 v->vehstatus |= VS_CRASHED; |
|
2681 END_ENUM_WAGONS(v) |
|
2682 |
|
2683 InvalidateWindowWidget(WC_VEHICLE_VIEW, u->index, STATUS_BAR); |
|
2684 } |
|
2685 |
|
2686 static uint CountPassengersInTrain(const Vehicle* v) |
|
2687 { |
|
2688 uint num = 0; |
|
2689 BEGIN_ENUM_WAGONS(v) |
|
2690 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) num += v->cargo.Count(); |
|
2691 END_ENUM_WAGONS(v) |
|
2692 return num; |
|
2693 } |
|
2694 |
2662 struct TrainCollideChecker { |
2695 struct TrainCollideChecker { |
2663 const Vehicle *v; |
2696 Vehicle *v; |
2664 const Vehicle *v_skip; |
2697 const Vehicle *v_skip; |
|
2698 uint num; |
2665 }; |
2699 }; |
2666 |
2700 |
2667 static void *FindTrainCollideEnum(Vehicle *v, void *data) |
2701 static void *FindTrainCollideEnum(Vehicle *v, void *data) |
2668 { |
2702 { |
2669 const TrainCollideChecker* tcc = (TrainCollideChecker*)data; |
2703 TrainCollideChecker* tcc = (TrainCollideChecker*)data; |
2670 |
2704 |
2671 if (v != tcc->v && |
2705 if (v != tcc->v && |
2672 v != tcc->v_skip && |
2706 v != tcc->v_skip && |
2673 v->type == VEH_TRAIN && |
2707 v->type == VEH_TRAIN && |
2674 v->u.rail.track != TRACK_BIT_DEPOT && |
2708 v->u.rail.track != TRACK_BIT_DEPOT && |
2675 myabs(v->z_pos - tcc->v->z_pos) < 6 && |
2709 myabs(v->z_pos - tcc->v->z_pos) < 6 && |
2676 myabs(v->x_pos - tcc->v->x_pos) < 6 && |
2710 myabs(v->x_pos - tcc->v->x_pos) < 6 && |
2677 myabs(v->y_pos - tcc->v->y_pos) < 6) { |
2711 myabs(v->y_pos - tcc->v->y_pos) < 6 ) { |
2678 return v; |
2712 |
2679 } else { |
2713 Vehicle *coll = GetFirstVehicleInChain(v); |
2680 return NULL; |
2714 |
2681 } |
2715 /* it can't collide with its own wagons */ |
2682 } |
2716 if (tcc->v == coll || |
2683 |
2717 (tcc->v->u.rail.track == TRACK_BIT_WORMHOLE && (tcc->v->direction & 2) != (v->direction & 2))) |
2684 static void SetVehicleCrashed(Vehicle *v) |
2718 return NULL; |
2685 { |
2719 |
2686 if (v->u.rail.crash_anim_pos != 0) return; |
2720 /* two drivers + passengers killed in train tcc->v (if it was not crashed already) */ |
2687 |
2721 if (!(tcc->v->vehstatus & VS_CRASHED)) { |
2688 v->u.rail.crash_anim_pos++; |
2722 tcc->num += 2 + CountPassengersInTrain(tcc->v); |
2689 |
2723 SetVehicleCrashed(tcc->v); |
2690 Vehicle *u = v; |
2724 } |
2691 BEGIN_ENUM_WAGONS(v) |
2725 |
2692 v->vehstatus |= VS_CRASHED; |
2726 if (!(coll->vehstatus & VS_CRASHED)) { |
2693 END_ENUM_WAGONS(v) |
2727 /* two drivers + passengers killed in train coll (if it was not crashed already) */ |
2694 |
2728 tcc->num += 2 + CountPassengersInTrain(coll); |
2695 InvalidateWindowWidget(WC_VEHICLE_VIEW, u->index, STATUS_BAR); |
2729 SetVehicleCrashed(coll); |
2696 } |
2730 } |
2697 |
2731 } |
2698 static uint CountPassengersInTrain(const Vehicle* v) |
2732 |
2699 { |
2733 return NULL; |
2700 uint num = 0; |
|
2701 BEGIN_ENUM_WAGONS(v) |
|
2702 if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) num += v->cargo_count; |
|
2703 END_ENUM_WAGONS(v) |
|
2704 return num; |
|
2705 } |
2734 } |
2706 |
2735 |
2707 /** |
2736 /** |
2708 * Checks whether the specified train has a collision with another vehicle. If |
2737 * Checks whether the specified train has a collision with another vehicle. If |
2709 * so, destroys this vehicle, and the other vehicle if its subtype has TS_Front. |
2738 * so, destroys this vehicle, and the other vehicle if its subtype has TS_Front. |
2718 assert(v->u.rail.track == TRACK_BIT_WORMHOLE || TileVirtXY(v->x_pos, v->y_pos) == v->tile); |
2747 assert(v->u.rail.track == TRACK_BIT_WORMHOLE || TileVirtXY(v->x_pos, v->y_pos) == v->tile); |
2719 |
2748 |
2720 TrainCollideChecker tcc; |
2749 TrainCollideChecker tcc; |
2721 tcc.v = v; |
2750 tcc.v = v; |
2722 tcc.v_skip = v->next; |
2751 tcc.v_skip = v->next; |
2723 |
2752 tcc.num = 0; |
2724 /* find colliding vehicle */ |
2753 |
2725 Vehicle *realcoll = (Vehicle*)VehicleFromPosXY(v->x_pos, v->y_pos, &tcc, FindTrainCollideEnum); |
2754 /* find colliding vehicles */ |
2726 if (realcoll == NULL) return; |
2755 VehicleFromPosXY(v->x_pos, v->y_pos, &tcc, FindTrainCollideEnum); |
2727 |
2756 |
2728 Vehicle *coll = GetFirstVehicleInChain(realcoll); |
2757 /* any dead -> no crash */ |
2729 |
2758 if (tcc.num == 0) return; |
2730 /* it can't collide with its own wagons */ |
2759 |
2731 if (v == coll || |
2760 SetDParam(0, tcc.num); |
2732 (v->u.rail.track == TRACK_BIT_WORMHOLE && (v->direction & 2) != (realcoll->direction & 2))) |
|
2733 return; |
|
2734 |
|
2735 /* two drivers + passengers killed in train v */ |
|
2736 uint num = 2 + CountPassengersInTrain(v); |
|
2737 if (!(coll->vehstatus & VS_CRASHED)) |
|
2738 /* two drivers + passengers killed in train coll (if it was not crashed already) */ |
|
2739 num += 2 + CountPassengersInTrain(coll); |
|
2740 |
|
2741 SetVehicleCrashed(v); |
|
2742 if (IsFrontEngine(coll)) SetVehicleCrashed(coll); |
|
2743 |
|
2744 SetDParam(0, num); |
|
2745 AddNewsItem(STR_8868_TRAIN_CRASH_DIE_IN_FIREBALL, |
2761 AddNewsItem(STR_8868_TRAIN_CRASH_DIE_IN_FIREBALL, |
2746 NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_VEHICLE, NT_ACCIDENT, 0), |
2762 NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_VEHICLE, NT_ACCIDENT, 0), |
2747 v->index, |
2763 v->index, |
2748 0 |
2764 0 |
2749 ); |
2765 ); |
3282 } |
3298 } |
3283 |
3299 |
3284 |
3300 |
3285 void Train_Tick(Vehicle *v) |
3301 void Train_Tick(Vehicle *v) |
3286 { |
3302 { |
3287 if (_age_cargo_skip_counter == 0 && v->cargo_days != 0xff) |
3303 if (_age_cargo_skip_counter == 0) v->cargo.AgeCargo(); |
3288 v->cargo_days++; |
|
3289 |
3304 |
3290 v->tick_counter++; |
3305 v->tick_counter++; |
3291 |
3306 |
3292 if (IsFrontEngine(v)) { |
3307 if (IsFrontEngine(v)) { |
|
3308 v->current_order_time++; |
|
3309 |
3293 TrainLocoHandler(v, false); |
3310 TrainLocoHandler(v, false); |
3294 |
3311 |
3295 /* make sure vehicle wasn't deleted. */ |
3312 /* make sure vehicle wasn't deleted. */ |
3296 if (v->type == VEH_TRAIN && IsFrontEngine(v)) |
3313 if (v->type == VEH_TRAIN && IsFrontEngine(v)) |
3297 TrainLocoHandler(v, true); |
3314 TrainLocoHandler(v, true); |
3351 v->current_order.dest = depot->index; |
3368 v->current_order.dest = depot->index; |
3352 v->dest_tile = tfdd.tile; |
3369 v->dest_tile = tfdd.tile; |
3353 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
3370 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); |
3354 } |
3371 } |
3355 |
3372 |
3356 int32 GetTrainRunningCost(const Vehicle *v) |
3373 Money GetTrainRunningCost(const Vehicle *v) |
3357 { |
3374 { |
3358 int32 cost = 0; |
3375 Money cost = 0; |
3359 |
3376 |
3360 do { |
3377 do { |
3361 const RailVehicleInfo *rvi = RailVehInfo(v->engine_type); |
3378 const RailVehicleInfo *rvi = RailVehInfo(v->engine_type); |
3362 |
3379 |
3363 byte cost_factor = GetVehicleProperty(v, 0x0D, rvi->running_cost_base); |
3380 byte cost_factor = GetVehicleProperty(v, 0x0D, rvi->running_cost_base); |
3387 if (tile != 0) v->dest_tile = tile; |
3404 if (tile != 0) v->dest_tile = tile; |
3388 } |
3405 } |
3389 |
3406 |
3390 if ((v->vehstatus & VS_STOPPED) == 0) { |
3407 if ((v->vehstatus & VS_STOPPED) == 0) { |
3391 /* running costs */ |
3408 /* running costs */ |
3392 int32 cost = GetTrainRunningCost(v) / 364; |
3409 CommandCost cost(GetTrainRunningCost(v) / 364); |
3393 |
3410 |
3394 v->profit_this_year -= cost >> 8; |
3411 v->profit_this_year -= cost.GetCost() >> 8; |
3395 |
3412 |
3396 SET_EXPENSES_TYPE(EXPENSES_TRAIN_RUN); |
3413 SET_EXPENSES_TYPE(EXPENSES_TRAIN_RUN); |
3397 SubtractMoneyFromPlayerFract(v->owner, cost); |
3414 SubtractMoneyFromPlayerFract(v->owner, cost); |
3398 |
3415 |
3399 InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
3416 InvalidateWindow(WC_VEHICLE_DETAILS, v->index); |
3400 InvalidateWindowClasses(WC_TRAINS_LIST); |
3417 InvalidateWindowClasses(WC_TRAINS_LIST); |
3401 } |
3418 } |
|
3419 } else if (IsTrainEngine(v)) { |
|
3420 /* Also age engines that aren't front engines */ |
|
3421 AgeVehicle(v); |
3402 } |
3422 } |
3403 } |
3423 } |
3404 |
3424 |
3405 void TrainsYearlyLoop() |
3425 void TrainsYearlyLoop() |
3406 { |
3426 { |