comparison src/train_cmd.cpp @ 12237:52ee820c1243 draft

(svn r16652) -Codechange: use less strict, but faster check for quickly bailing out in FindTrainCollideEnum() (Bilbo) -Codechange: shuffle the code a bit
author smatz <smatz@openttd.org>
date Wed, 24 Jun 2009 23:59:20 +0000
parents 2f16fe37d1b8
children 75aa2b403c34
comparison
equal deleted inserted replaced
12236:cc6e71b54c8a 12237:52ee820c1243
3528 * @param v first vehicle of chain 3528 * @param v first vehicle of chain
3529 * @return number of victims (including 2 drivers; zero if train was already crashed) 3529 * @return number of victims (including 2 drivers; zero if train was already crashed)
3530 */ 3530 */
3531 static uint TrainCrashed(Train *v) 3531 static uint TrainCrashed(Train *v)
3532 { 3532 {
3533 /* Try to re-reserve track under already crashed train too */
3534 for (const Train *u = v; u != NULL; u = u->Next()) {
3535 TrackBits trackbits = u->track;
3536 if (trackbits == TRACK_BIT_WORMHOLE) {
3537 /* Vehicle is inside a wormhole, v->track contains no useful value then. */
3538 trackbits = DiagDirToDiagTrackBits(GetTunnelBridgeDirection(u->tile));
3539 }
3540 TryReserveRailTrack(u->tile, TrackBitsToTrack(trackbits));
3541 }
3542
3533 /* do not crash train twice */ 3543 /* do not crash train twice */
3534 if (v->vehstatus & VS_CRASHED) return 0; 3544 if (v->vehstatus & VS_CRASHED) return 0;
3535 3545
3536 /* two drivers + passengers */ 3546 /* two drivers + passengers */
3537 uint num = 2 + CountPassengersInTrain(v); 3547 uint num = 2 + CountPassengersInTrain(v);
3541 3551
3542 return num; 3552 return num;
3543 } 3553 }
3544 3554
3545 struct TrainCollideChecker { 3555 struct TrainCollideChecker {
3546 Vehicle *v; ///< vehicle we are testing for collision 3556 Train *v; ///< vehicle we are testing for collision
3547 uint num; ///< number of dead if train collided 3557 uint num; ///< number of victims if train collided
3548 }; 3558 };
3549 3559
3550 static Vehicle *FindTrainCollideEnum(Vehicle *v, void *data) 3560 static Vehicle *FindTrainCollideEnum(Vehicle *v, void *data)
3551 { 3561 {
3552 TrainCollideChecker *tcc = (TrainCollideChecker*)data; 3562 TrainCollideChecker *tcc = (TrainCollideChecker*)data;
3553 3563
3554 if (v->type != VEH_TRAIN) return NULL; 3564 /* not a train or in depot */
3565 if (v->type != VEH_TRAIN || Train::From(v)->track == TRACK_BIT_DEPOT) return NULL;
3555 3566
3556 /* get first vehicle now to make most usual checks faster */ 3567 /* get first vehicle now to make most usual checks faster */
3557 Vehicle *coll = v->First(); 3568 Train *coll = Train::From(v)->First();
3558 3569
3559 /* can't collide with own wagons && can't crash in depot && the same height level */ 3570 /* can't collide with own wagons */
3560 if (coll != tcc->v && Train::From(v)->track != TRACK_BIT_DEPOT && abs(v->z_pos - tcc->v->z_pos) < 6) { 3571 if (coll == tcc->v) return NULL;
3561 int x_diff = v->x_pos - tcc->v->x_pos; 3572
3562 int y_diff = v->y_pos - tcc->v->y_pos; 3573 int x_diff = v->x_pos - tcc->v->x_pos;
3563 3574 int y_diff = v->y_pos - tcc->v->y_pos;
3564 /* Needed to disable possible crash of competitor train in station by building diagonal track at its end. 3575
3565 * The second check is false when (abs(first), abs(second)) is: a) 5, 0 b) 4, <=3 c) 3, <=4, d) 2-, <=5 3576 /* Do fast calculation to check whether trains are not in close vicinity
3566 * Sum of these two is at most 2 + 5 == 4 + 3 == 7. So we can just test if sum of abs() is > 7 to prevent 3577 * and quickly reject trains distant enough for any collision.
3567 * multiplying. Simply, when sum of abs() is >= 8, the sum of squares can't be <= 25. 3578 * Differences are shifted by 7, mapping range [-7 .. 8] into [0 .. 15]
3568 * Even gcc3.4 seems to do abs() branchless (using arithmetics or conditional moves). */ 3579 * Differences are then ORed and then we check for any higher bits */
3569 if (abs(x_diff) + abs(y_diff) > 7 || x_diff * x_diff + y_diff * y_diff > 25) return NULL; 3580 uint hash = (y_diff + 7) | (x_diff + 7);
3570 3581 if (hash & ~15) return NULL;
3571 /* crash both trains */ 3582
3572 tcc->num += TrainCrashed(Train::From(tcc->v)); 3583 /* Slower check using multiplication */
3573 tcc->num += TrainCrashed(Train::From(coll)); 3584 if (x_diff * x_diff + y_diff * y_diff > 25) return NULL;
3574 3585
3575 /* Try to reserve all tiles directly under the crashed trains. 3586 /* Happens when there is a train under bridge next to bridge head */
3576 * As there might be more than two trains involved, we have to do that for all vehicles */ 3587 if (abs(v->z_pos - tcc->v->z_pos) > 5) return NULL;
3577 const Train *u; 3588
3578 FOR_ALL_TRAINS(u) { 3589 /* crash both trains */
3579 if ((u->vehstatus & VS_CRASHED) && u->track != TRACK_BIT_DEPOT) { 3590 tcc->num += TrainCrashed(tcc->v);
3580 TrackBits trackbits = u->track; 3591 tcc->num += TrainCrashed(coll);
3581 if (trackbits == TRACK_BIT_WORMHOLE) { 3592
3582 /* Vehicle is inside a wormhole, v->track contains no useful value then. */ 3593 return NULL; // continue searching
3583 trackbits = DiagDirToDiagTrackBits(GetTunnelBridgeDirection(u->tile));
3584 }
3585 TryReserveRailTrack(u->tile, TrackBitsToTrack(trackbits));
3586 }
3587 }
3588 }
3589
3590 return NULL;
3591 } 3594 }
3592 3595
3593 /** 3596 /**
3594 * Checks whether the specified train has a collision with another vehicle. If 3597 * Checks whether the specified train has a collision with another vehicle. If
3595 * so, destroys this vehicle, and the other vehicle if its subtype has TS_Front. 3598 * so, destroys this vehicle, and the other vehicle if its subtype has TS_Front.