|
@@ -2796,56 +2796,58 @@ static void SetVehicleCrashed(Vehicle *v
|
|
|
|
|
|
static uint CountPassengersInTrain(const Vehicle* v)
|
|
|
{
|
|
|
uint num = 0;
|
|
|
BEGIN_ENUM_WAGONS(v)
|
|
|
if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) num += v->cargo.Count();
|
|
|
END_ENUM_WAGONS(v)
|
|
|
return num;
|
|
|
}
|
|
|
|
|
|
struct TrainCollideChecker {
|
|
|
Vehicle *v;
|
|
|
uint num;
|
|
|
};
|
|
|
|
|
|
static void *FindTrainCollideEnum(Vehicle *v, void *data)
|
|
|
{
|
|
|
TrainCollideChecker* tcc = (TrainCollideChecker*)data;
|
|
|
|
|
|
if (v->type != VEH_TRAIN) return NULL;
|
|
|
|
|
|
/* get first vehicle now to make most usual checks faster */
|
|
|
Vehicle *coll = v->First();
|
|
|
|
|
|
/* can't collide with own wagons && can't crash in depot && not too far */
|
|
|
if (coll != tcc->v && v->u.rail.track != TRACK_BIT_DEPOT &&
|
|
|
abs(v->z_pos - tcc->v->z_pos) < 6 &&
|
|
|
abs(v->x_pos - tcc->v->x_pos) < 6 &&
|
|
|
abs(v->y_pos - tcc->v->y_pos) < 6 ) {
|
|
|
|
|
|
/* two drivers + passengers killed in train tcc->v (if it was not crashed already) */
|
|
|
/* can't collide with own wagons && can't crash in depot && the same height level */
|
|
|
if (coll != tcc->v && v->u.rail.track != TRACK_BIT_DEPOT && abs(v->z_pos - tcc->v->z_pos) < 6) {
|
|
|
int x_diff = v->x_pos - tcc->v->x_pos;
|
|
|
int y_diff = v->y_pos - tcc->v->y_pos;
|
|
|
|
|
|
/* needed to disable possible crash of competitor train in station by building diagonal track at its end */
|
|
|
if (x_diff * x_diff + y_diff * y_diff > 25) return NULL;
|
|
|
|
|
|
if (!(tcc->v->vehstatus & VS_CRASHED)) {
|
|
|
/* two drivers + passengers killed in train tcc->v (if it was not crashed already) */
|
|
|
tcc->num += 2 + CountPassengersInTrain(tcc->v);
|
|
|
SetVehicleCrashed(tcc->v);
|
|
|
}
|
|
|
|
|
|
if (!(coll->vehstatus & VS_CRASHED)) {
|
|
|
/* two drivers + passengers killed in train coll (if it was not crashed already) */
|
|
|
tcc->num += 2 + CountPassengersInTrain(coll);
|
|
|
SetVehicleCrashed(coll);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Checks whether the specified train has a collision with another vehicle. If
|
|
|
* so, destroys this vehicle, and the other vehicle if its subtype has TS_Front.
|
|
|
* Reports the incident in a flashy news item, modifies station ratings and
|
|
|
* plays a sound.
|
|
|
*/
|
|
|
static void CheckTrainCollision(Vehicle *v)
|
|
|
{
|
|
|
/* can't collide in depot */
|
|
|
if (v->u.rail.track == TRACK_BIT_DEPOT) return;
|
|
@@ -3225,89 +3227,90 @@ static void HandleBrokenTrain(Vehicle *v
|
|
|
|
|
|
InvalidateWindow(WC_VEHICLE_VIEW, v->index);
|
|
|
InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
|
|
|
|
|
|
if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
|
|
|
SndPlayVehicleFx((_opt.landscape != LT_TOYLAND) ?
|
|
|
SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
|
|
|
}
|
|
|
|
|
|
if (!(v->vehstatus & VS_HIDDEN)) {
|
|
|
Vehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
|
|
|
if (u != NULL) u->u.special.animation_state = v->breakdown_delay * 2;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (!(v->tick_counter & 3)) {
|
|
|
if (!--v->breakdown_delay) {
|
|
|
v->breakdown_ctr = 0;
|
|
|
InvalidateWindow(WC_VEHICLE_VIEW, v->index);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/** Maximum speeds for train that is broken down or approaching line end */
|
|
|
static const byte _breakdown_speeds[16] = {
|
|
|
static const uint16 _breakdown_speeds[16] = {
|
|
|
225, 210, 195, 180, 165, 150, 135, 120, 105, 90, 75, 60, 45, 30, 15, 15
|
|
|
};
|
|
|
|
|
|
|
|
|
/**
|
|
|
* Train is approaching line end, slow down and possibly reverse
|
|
|
*
|
|
|
* @param v front train engine
|
|
|
* @param signal not line end, just a red signal
|
|
|
* @return true iff we did NOT have to reverse
|
|
|
*/
|
|
|
static bool TrainApproachingLineEnd(Vehicle *v, bool signal)
|
|
|
{
|
|
|
/* Calc position within the current tile */
|
|
|
uint x = v->x_pos & 0xF;
|
|
|
uint y = v->y_pos & 0xF;
|
|
|
|
|
|
/* for diagonal directions, 'x' will be 0..15 -
|
|
|
* for other directions, it will be 1, 3, 5, ..., 15 */
|
|
|
switch (v->direction) {
|
|
|
case DIR_N : x = ~x + ~y + 24; break;
|
|
|
case DIR_N : x = ~x + ~y + 25; break;
|
|
|
case DIR_NW: x = y; /* FALLTHROUGH */
|
|
|
case DIR_NE: x = ~x + 16; break;
|
|
|
case DIR_E : x = ~x + y + 8; break;
|
|
|
case DIR_E : x = ~x + y + 9; break;
|
|
|
case DIR_SE: x = y; break;
|
|
|
case DIR_S : x = x + y - 8; break;
|
|
|
case DIR_W : x = ~y + x + 8; break;
|
|
|
case DIR_S : x = x + y - 7; break;
|
|
|
case DIR_W : x = ~y + x + 9; break;
|
|
|
default: break;
|
|
|
}
|
|
|
|
|
|
/* do not reverse when approaching red signal */
|
|
|
if (!signal && x + 4 >= TILE_SIZE) {
|
|
|
/* we are too near the tile end, reverse now */
|
|
|
v->cur_speed = 0;
|
|
|
ReverseTrainDirection(v);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
/* slow down */
|
|
|
v->vehstatus |= VS_TRAIN_SLOWING;
|
|
|
uint16 break_speed = _breakdown_speeds[x & 0xF];
|
|
|
if (!(v->direction & 1)) break_speed >>= 1;
|
|
|
if (break_speed < v->cur_speed) v->cur_speed = break_speed;
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
/**
|
|
|
* Checks for line end. Also, bars crossing at next tile if needed
|
|
|
*
|
|
|
* @param v vehicle we are checking
|
|
|
* @return true iff we did NOT have to reverse
|
|
|
*/
|
|
|
static bool TrainCheckIfLineEnds(Vehicle *v)
|
|
|
{
|
|
|
/* First, handle broken down train */
|
|
|
|
|
|
int t = v->breakdown_ctr;
|
|
|
if (t > 1) {
|
|
|
v->vehstatus |= VS_TRAIN_SLOWING;
|
|
|
|
|
|
uint16 break_speed = _breakdown_speeds[GB(~t, 4, 4)];
|
|
|
if (break_speed < v->cur_speed) v->cur_speed = break_speed;
|
|
|
} else {
|
|
|
v->vehstatus &= ~VS_TRAIN_SLOWING;
|