src/train_cmd.cpp
changeset 8830 4184b599c73d
parent 8813 6d054db96ede
child 8838 1549b7f9d0a8
equal deleted inserted replaced
8829:9152e779e7da 8830:4184b599c73d
    51 #include "table/strings.h"
    51 #include "table/strings.h"
    52 #include "table/train_cmd.h"
    52 #include "table/train_cmd.h"
    53 
    53 
    54 static bool TrainCheckIfLineEnds(Vehicle *v);
    54 static bool TrainCheckIfLineEnds(Vehicle *v);
    55 static void TrainController(Vehicle *v, bool update_image);
    55 static void TrainController(Vehicle *v, bool update_image);
       
    56 static TileIndex TrainApproachingCrossingTile(const Vehicle *v);
    56 
    57 
    57 static const byte _vehicle_initial_x_fract[4] = {10, 8, 4,  8};
    58 static const byte _vehicle_initial_x_fract[4] = {10, 8, 4,  8};
    58 static const byte _vehicle_initial_y_fract[4] = { 8, 4, 8, 10};
    59 static const byte _vehicle_initial_y_fract[4] = { 8, 4, 8, 10};
    59 
    60 
    60 
    61 
  1622 
  1623 
  1623 	/* Update train's power incase tiles were different rail type */
  1624 	/* Update train's power incase tiles were different rail type */
  1624 	TrainPowerChanged(v);
  1625 	TrainPowerChanged(v);
  1625 }
  1626 }
  1626 
  1627 
  1627 /* Check if the vehicle is a train and is on the tile we are testing */
  1628 
  1628 static void *TestTrainOnCrossing(Vehicle *v, void *data)
  1629 /**
  1629 {
  1630  * Check if the vehicle is a train
  1630 	if (v->type != VEH_TRAIN) return NULL;
  1631  * @param v vehicle on tile
       
  1632  * @return v if it is a train, NULL otherwise
       
  1633  */
       
  1634 static void *TrainOnTileEnum(Vehicle *v, void *)
       
  1635 {
       
  1636 	return (v->type == VEH_TRAIN) ? v : NULL;
       
  1637 }
       
  1638 
       
  1639 
       
  1640 /**
       
  1641  * Checks if a train is approaching a rail-road crossing
       
  1642  * @param v vehicle on tile
       
  1643  * @param data tile with crossing we are testing
       
  1644  * @return v if it is approaching a crossing, NULL otherwise
       
  1645  */
       
  1646 static void *TrainApproachingCrossingEnum(Vehicle *v, void *data)
       
  1647 {
       
  1648 	/* not a train || not front engine || crashed */
       
  1649 	if (v->type != VEH_TRAIN || !IsFrontEngine(v) || v->vehstatus & VS_CRASHED) return NULL;
       
  1650 
       
  1651 	TileIndex tile = *(TileIndex*)data;
       
  1652 
       
  1653 	if (TrainApproachingCrossingTile(v) != tile) return NULL;
       
  1654 
  1631 	return v;
  1655 	return v;
  1632 }
  1656 }
  1633 
  1657 
  1634 static void DisableTrainCrossing(TileIndex tile)
  1658 
  1635 {
  1659 /**
  1636 	if (IsLevelCrossingTile(tile) &&
  1660  * Finds a vehicle approaching rail-road crossing
  1637 			IsCrossingBarred(tile) &&
  1661  * @param tile tile to test
  1638 			VehicleFromPos(tile, NULL, &TestTrainOnCrossing) == NULL) { // empty?
  1662  * @return pointer to vehicle approaching the crossing
  1639 		UnbarCrossing(tile);
  1663  * @pre tile is a rail-road crossing
  1640 		MarkTileDirtyByTile(tile);
  1664  */
  1641 	}
  1665 static Vehicle *TrainApproachingCrossing(TileIndex tile)
  1642 }
  1666 {
       
  1667 	assert(IsLevelCrossingTile(tile));
       
  1668 
       
  1669 	DiagDirection dir = AxisToDiagDir(OtherAxis(GetCrossingRoadAxis(tile)));
       
  1670 	TileIndex tile_from = tile + TileOffsByDiagDir(dir);
       
  1671 
       
  1672 	Vehicle *v = (Vehicle *)VehicleFromPos(tile_from, &tile, &TrainApproachingCrossingEnum);
       
  1673 
       
  1674 	if (v != NULL) return v;
       
  1675 
       
  1676 	dir = ReverseDiagDir(dir);
       
  1677 	tile_from = tile + TileOffsByDiagDir(dir);
       
  1678 
       
  1679 	return (Vehicle *)VehicleFromPos(tile_from, &tile, &TrainApproachingCrossingEnum);
       
  1680 }
       
  1681 
       
  1682 
       
  1683 /**
       
  1684  * Sets correct crossing state
       
  1685  * @param tile tile to update
       
  1686  * @pre tile is a rail-road crossing
       
  1687  */
       
  1688 void UpdateTrainCrossing(TileIndex tile)
       
  1689 {
       
  1690 	assert(IsLevelCrossingTile(tile));
       
  1691 
       
  1692 	UnbarCrossing(tile);
       
  1693 
       
  1694 	/* train on crossing || train approaching crossing */
       
  1695 	if (VehicleFromPos(tile, NULL, &TrainOnTileEnum) || TrainApproachingCrossing(tile)) {
       
  1696 		BarCrossing(tile);
       
  1697 	}
       
  1698 
       
  1699 	MarkTileDirtyByTile(tile);
       
  1700 }
       
  1701 
  1643 
  1702 
  1644 /**
  1703 /**
  1645  * Advances wagons for train reversing, needed for variable length wagons.
  1704  * Advances wagons for train reversing, needed for variable length wagons.
  1646  * Needs to be called once before the train is reversed, and once after it.
  1705  * Needs to be called once before the train is reversed, and once after it.
  1647  * @param v First vehicle in chain
  1706  * @param v First vehicle in chain
  1686 	if (IsTileDepotType(v->tile, TRANSPORT_RAIL)) {
  1745 	if (IsTileDepotType(v->tile, TRANSPORT_RAIL)) {
  1687 		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
  1746 		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
  1688 	}
  1747 	}
  1689 
  1748 
  1690 	/* Check if we were approaching a rail/road-crossing */
  1749 	/* Check if we were approaching a rail/road-crossing */
  1691 	{
  1750 	TileIndex crossing = TrainApproachingCrossingTile(v);
  1692 		/* Determine the diagonal direction in which we will exit this tile */
       
  1693 		DiagDirection dir = TrainExitDir(v->direction, v->u.rail.track);
       
  1694 		/* Calculate next tile */
       
  1695 		TileIndex tile = v->tile + TileOffsByDiagDir(dir);
       
  1696 		/* Check if the train left a rail/road-crossing */
       
  1697 		DisableTrainCrossing(tile);
       
  1698 	}
       
  1699 
  1751 
  1700 	/* count number of vehicles */
  1752 	/* count number of vehicles */
  1701 	int r = 0;  ///< number of vehicles - 1
  1753 	int r = 0;  ///< number of vehicles - 1
  1702 	for (const Vehicle *u = v; (u = u->Next()) != NULL;) { r++; }
  1754 	for (const Vehicle *u = v; (u = u->Next()) != NULL;) { r++; }
  1703 
  1755 
  1717 
  1769 
  1718 	/* update all images */
  1770 	/* update all images */
  1719 	for (Vehicle *u = v; u != NULL; u = u->Next()) { u->cur_image = u->GetImage(u->direction); }
  1771 	for (Vehicle *u = v; u != NULL; u = u->Next()) { u->cur_image = u->GetImage(u->direction); }
  1720 
  1772 
  1721 	ClrBit(v->u.rail.flags, VRF_REVERSING);
  1773 	ClrBit(v->u.rail.flags, VRF_REVERSING);
       
  1774 
       
  1775 	/* update crossing we were approaching */
       
  1776 	if (crossing != INVALID_TILE) UpdateTrainCrossing(crossing);
       
  1777 
       
  1778 	/* maybe we are approaching crossing now, after reversal */
       
  1779 	crossing = TrainApproachingCrossingTile(v);
       
  1780 	if (crossing != INVALID_TILE) UpdateTrainCrossing(crossing);
  1722 }
  1781 }
  1723 
  1782 
  1724 /** Reverse train.
  1783 /** Reverse train.
  1725  * @param tile unused
  1784  * @param tile unused
  1726  * @param flags type of operation
  1785  * @param flags type of operation
  2775 
  2834 
  2776 static void SetVehicleCrashed(Vehicle *v)
  2835 static void SetVehicleCrashed(Vehicle *v)
  2777 {
  2836 {
  2778 	if (v->u.rail.crash_anim_pos != 0) return;
  2837 	if (v->u.rail.crash_anim_pos != 0) return;
  2779 
  2838 
       
  2839 	/* we may need to update crossing we were approaching */
       
  2840 	TileIndex crossing = TrainApproachingCrossingTile(v);
       
  2841 
  2780 	v->u.rail.crash_anim_pos++;
  2842 	v->u.rail.crash_anim_pos++;
  2781 
  2843 
  2782 	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
  2844 	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
  2783 	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
  2845 	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
  2784 
  2846 
  2790 
  2852 
  2791 	BEGIN_ENUM_WAGONS(v)
  2853 	BEGIN_ENUM_WAGONS(v)
  2792 		v->vehstatus |= VS_CRASHED;
  2854 		v->vehstatus |= VS_CRASHED;
  2793 		MarkSingleVehicleDirty(v);
  2855 		MarkSingleVehicleDirty(v);
  2794 	END_ENUM_WAGONS(v)
  2856 	END_ENUM_WAGONS(v)
       
  2857 
       
  2858 	/* must be updated after the train has been marked crashed */
       
  2859 	if (crossing != INVALID_TILE) UpdateTrainCrossing(crossing);
  2795 }
  2860 }
  2796 
  2861 
  2797 static uint CountPassengersInTrain(const Vehicle* v)
  2862 static uint CountPassengersInTrain(const Vehicle* v)
  2798 {
  2863 {
  2799 	uint num = 0;
  2864 	uint num = 0;
  2897 	Vehicle *prev;
  2962 	Vehicle *prev;
  2898 
  2963 
  2899 	/* For every vehicle after and including the given vehicle */
  2964 	/* For every vehicle after and including the given vehicle */
  2900 	for (prev = v->Previous(); v != NULL; prev = v, v = v->Next()) {
  2965 	for (prev = v->Previous(); v != NULL; prev = v, v = v->Next()) {
  2901 		DiagDirection enterdir = DIAGDIR_BEGIN;
  2966 		DiagDirection enterdir = DIAGDIR_BEGIN;
  2902 		bool update_signals = false;
  2967 		bool update_signals_crossing = false; // will we update signals or crossing state?
  2903 		BeginVehicleMove(v);
  2968 		BeginVehicleMove(v);
  2904 
  2969 
  2905 		GetNewVehiclePosResult gp = GetNewVehiclePos(v);
  2970 		GetNewVehiclePosResult gp = GetNewVehiclePos(v);
  2906 		if (v->u.rail.track != TRACK_BIT_WORMHOLE) {
  2971 		if (v->u.rail.track != TRACK_BIT_WORMHOLE) {
  2907 			/* Not inside tunnel */
  2972 			/* Not inside tunnel */
  3020 				uint32 r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
  3085 				uint32 r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
  3021 				if (HasBit(r, VETS_CANNOT_ENTER)) {
  3086 				if (HasBit(r, VETS_CANNOT_ENTER)) {
  3022 					goto invalid_rail;
  3087 					goto invalid_rail;
  3023 				}
  3088 				}
  3024 
  3089 
  3025 				if (IsLevelCrossingTile(v->tile) && v->Next() == NULL) {
       
  3026 					UnbarCrossing(v->tile);
       
  3027 					MarkTileDirtyByTile(v->tile);
       
  3028 				}
       
  3029 
       
  3030 				if (IsFrontEngine(v)) v->load_unload_time_rem = 0;
  3090 				if (IsFrontEngine(v)) v->load_unload_time_rem = 0;
  3031 
  3091 
  3032 				if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
  3092 				if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
  3033 					v->tile = gp.new_tile;
  3093 					v->tile = gp.new_tile;
  3034 
  3094 
  3040 					assert(v->u.rail.track);
  3100 					assert(v->u.rail.track);
  3041 				}
  3101 				}
  3042 
  3102 
  3043 				/* We need to update signal status, but after the vehicle position hash
  3103 				/* We need to update signal status, but after the vehicle position hash
  3044 				 * has been updated by AfterSetTrainPos() */
  3104 				 * has been updated by AfterSetTrainPos() */
  3045 				update_signals = true;
  3105 				update_signals_crossing = true;
  3046 
  3106 
  3047 				if (prev == NULL) AffectSpeedByDirChange(v, chosen_dir);
  3107 				if (prev == NULL) AffectSpeedByDirChange(v, chosen_dir);
  3048 
  3108 
  3049 				v->direction = chosen_dir;
  3109 				v->direction = chosen_dir;
  3050 			}
  3110 			}
  3079 		if (prev == NULL) {
  3139 		if (prev == NULL) {
  3080 			/* This is the first vehicle in the train */
  3140 			/* This is the first vehicle in the train */
  3081 			AffectSpeedByZChange(v, old_z);
  3141 			AffectSpeedByZChange(v, old_z);
  3082 		}
  3142 		}
  3083 
  3143 
  3084 		if (update_signals) {
  3144 		if (update_signals_crossing) {
  3085 			if (IsFrontEngine(v)) TrainMovedChangeSignals(gp.new_tile, enterdir);
  3145 			if (IsFrontEngine(v)) TrainMovedChangeSignals(gp.new_tile, enterdir);
  3086 
  3146 
  3087 			/* Signals can only change when the first
  3147 			/* Signals can only change when the first
  3088 			 * (above) or the last vehicle moves. */
  3148 			 * (above) or the last vehicle moves. */
  3089 			if (v->Next() == NULL) TrainMovedChangeSignals(gp.old_tile, ReverseDiagDir(enterdir));
  3149 			if (v->Next() == NULL) {
       
  3150 				TrainMovedChangeSignals(gp.old_tile, ReverseDiagDir(enterdir));
       
  3151 				if (IsLevelCrossingTile(gp.old_tile)) UpdateTrainCrossing(gp.old_tile);
       
  3152 			}
  3090 		}
  3153 		}
  3091 	}
  3154 	}
  3092 	return;
  3155 	return;
  3093 
  3156 
  3094 invalid_rail:
  3157 invalid_rail:
  3145 	Owner owner = v->owner;
  3208 	Owner owner = v->owner;
  3146 
  3209 
  3147 	delete v;
  3210 	delete v;
  3148 	v = NULL; // make sure nobody will won't try to read 'v' anymore
  3211 	v = NULL; // make sure nobody will won't try to read 'v' anymore
  3149 
  3212 
  3150 	/* Check if the wagon was on a road/rail-crossing and disable it if no
  3213 	/* check if the wagon was on a road/rail-crossing */
  3151 	 * others are on it */
  3214 	if (IsLevelCrossingTile(tile)) UpdateTrainCrossing(tile);
  3152 	DisableTrainCrossing(tile);
       
  3153 
  3215 
  3154 	/* Update signals */
  3216 	/* Update signals */
  3155 	if (IsTileType(tile, MP_TUNNELBRIDGE) || IsTileDepotType(tile, TRANSPORT_RAIL)) {
  3217 	if (IsTileType(tile, MP_TUNNELBRIDGE) || IsTileDepotType(tile, TRANSPORT_RAIL)) {
  3156 		UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, owner);
  3218 		UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, owner);
  3157 	} else {
  3219 	} else {
  3294 	return true;
  3356 	return true;
  3295 }
  3357 }
  3296 
  3358 
  3297 
  3359 
  3298 /**
  3360 /**
       
  3361  * Determines whether train would like to leave the tile
       
  3362  * @param v train to test
       
  3363  * @return true iff vehicle is NOT entering or inside a depot or tunnel/bridge
       
  3364  */
       
  3365 static bool TrainCanLeaveTile(const Vehicle *v)
       
  3366 {
       
  3367 	/* Exit if inside a tunnel/bridge or a depot */
       
  3368 	if (v->u.rail.track == TRACK_BIT_WORMHOLE || v->u.rail.track == TRACK_BIT_DEPOT) return false;
       
  3369 
       
  3370 	TileIndex tile = v->tile;
       
  3371 
       
  3372 	/* entering a tunnel/bridge? */
       
  3373 	if (IsTileType(tile, MP_TUNNELBRIDGE)) {
       
  3374 		DiagDirection dir = GetTunnelBridgeDirection(tile);
       
  3375 		if (DiagDirToDir(dir) == v->direction) return false;
       
  3376 	}
       
  3377 
       
  3378 	/* entering a depot? */
       
  3379 	if (IsTileDepotType(tile, TRANSPORT_RAIL)) {
       
  3380 		DiagDirection dir = ReverseDiagDir(GetRailDepotDirection(tile));
       
  3381 		if (DiagDirToDir(dir) == v->direction) return false;
       
  3382 	}
       
  3383 
       
  3384 	return true;
       
  3385 }
       
  3386 
       
  3387 
       
  3388 /**
       
  3389  * Determines whether train is approaching a rail-road crossing
       
  3390  *   (thus making it barred)
       
  3391  * @param v front engine of train
       
  3392  * @return TileIndex of crossing the train is approaching, else INVALID_TILE
       
  3393  * @pre v in non-crashed front engine
       
  3394  */
       
  3395 static TileIndex TrainApproachingCrossingTile(const Vehicle *v)
       
  3396 {
       
  3397 	assert(IsFrontEngine(v));
       
  3398 	assert(!(v->vehstatus & VS_CRASHED));
       
  3399 
       
  3400 	if (!TrainCanLeaveTile(v)) return INVALID_TILE;
       
  3401 
       
  3402 	DiagDirection dir = TrainExitDir(v->direction, v->u.rail.track);
       
  3403 	TileIndex tile = v->tile + TileOffsByDiagDir(dir);
       
  3404 
       
  3405 	/* not a crossing || wrong axis || wrong railtype || wrong owner */
       
  3406 	if (!IsLevelCrossingTile(tile) || DiagDirToAxis(dir) == GetCrossingRoadAxis(tile) ||
       
  3407 			!CheckCompatibleRail(v, tile) || GetTileOwner(tile) != v->owner) {
       
  3408 		return INVALID_TILE;
       
  3409 	}
       
  3410 
       
  3411 	return tile;
       
  3412 }
       
  3413 
       
  3414 
       
  3415 /**
  3299  * Checks for line end. Also, bars crossing at next tile if needed
  3416  * Checks for line end. Also, bars crossing at next tile if needed
  3300  *
  3417  *
  3301  * @param v vehicle we are checking
  3418  * @param v vehicle we are checking
  3302  * @return true iff we did NOT have to reverse
  3419  * @return true iff we did NOT have to reverse
  3303  */
  3420  */
  3313 		if (break_speed < v->cur_speed) v->cur_speed = break_speed;
  3430 		if (break_speed < v->cur_speed) v->cur_speed = break_speed;
  3314 	} else {
  3431 	} else {
  3315 		v->vehstatus &= ~VS_TRAIN_SLOWING;
  3432 		v->vehstatus &= ~VS_TRAIN_SLOWING;
  3316 	}
  3433 	}
  3317 
  3434 
  3318 	/* Exit if inside a tunnel/bridge or a depot */
  3435 	if (!TrainCanLeaveTile(v)) return true;
  3319 	if (v->u.rail.track == TRACK_BIT_WORMHOLE || v->u.rail.track == TRACK_BIT_DEPOT) return true;
       
  3320 
       
  3321 	TileIndex tile = v->tile;
       
  3322 
       
  3323 	/* entering a tunnel/bridge? */
       
  3324 	if (IsTileType(tile, MP_TUNNELBRIDGE)) {
       
  3325 		DiagDirection dir = GetTunnelBridgeDirection(tile);
       
  3326 		if (DiagDirToDir(dir) == v->direction) return true;
       
  3327 	}
       
  3328 
       
  3329 	/* entering a depot? */
       
  3330 	if (IsTileDepotType(tile, TRANSPORT_RAIL)) {
       
  3331 		DiagDirection dir = ReverseDiagDir(GetRailDepotDirection(tile));
       
  3332 		if (DiagDirToDir(dir) == v->direction) return true;
       
  3333 	}
       
  3334 
  3436 
  3335 	/* Determine the non-diagonal direction in which we will exit this tile */
  3437 	/* Determine the non-diagonal direction in which we will exit this tile */
  3336 	DiagDirection dir = TrainExitDir(v->direction, v->u.rail.track);
  3438 	DiagDirection dir = TrainExitDir(v->direction, v->u.rail.track);
  3337 	/* Calculate next tile */
  3439 	/* Calculate next tile */
  3338 	tile += TileOffsByDiagDir(dir);
  3440 	TileIndex tile = v->tile + TileOffsByDiagDir(dir);
  3339 
  3441 
  3340 	/* Determine the track status on the next tile */
  3442 	/* Determine the track status on the next tile */
  3341 	uint32 ts = GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & _reachable_tracks[dir];
  3443 	uint32 ts = GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & _reachable_tracks[dir];
  3342 
  3444 
  3343 	/* We are sure the train is not entering a depot, it is detected above */
  3445 	/* We are sure the train is not entering a depot, it is detected above */