diff 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
line wrap: on
line diff
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -3530,6 +3530,16 @@
  */
 static uint TrainCrashed(Train *v)
 {
+	/* Try to re-reserve track under already crashed train too */
+	for (const Train *u = v; u != NULL; u = u->Next()) {
+		TrackBits trackbits = u->track;
+		if (trackbits == TRACK_BIT_WORMHOLE) {
+			/* Vehicle is inside a wormhole, v->track contains no useful value then. */
+			trackbits = DiagDirToDiagTrackBits(GetTunnelBridgeDirection(u->tile));
+		}
+		TryReserveRailTrack(u->tile, TrackBitsToTrack(trackbits));
+	}
+
 	/* do not crash train twice */
 	if (v->vehstatus & VS_CRASHED) return 0;
 
@@ -3543,51 +3553,44 @@
 }
 
 struct TrainCollideChecker {
-	Vehicle *v;  ///< vehicle we are testing for collision
-	uint num;    ///< number of dead if train collided
+	Train *v; ///< vehicle we are testing for collision
+	uint num; ///< number of victims if train collided
 };
 
 static Vehicle *FindTrainCollideEnum(Vehicle *v, void *data)
 {
 	TrainCollideChecker *tcc = (TrainCollideChecker*)data;
 
-	if (v->type != VEH_TRAIN) return NULL;
+	/* not a train or in depot */
+	if (v->type != VEH_TRAIN || Train::From(v)->track == TRACK_BIT_DEPOT) 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 && the same height level */
-	if (coll != tcc->v && Train::From(v)->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.
-		 * The second check is false when (abs(first), abs(second)) is: a) 5, 0  b) 4, <=3  c) 3, <=4, d) 2-, <=5
-		 * 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
-		 * multiplying. Simply, when sum of abs() is >= 8, the sum of squares can't be <= 25.
-		 * Even gcc3.4 seems to do abs() branchless (using arithmetics or conditional moves). */
-		if (abs(x_diff) + abs(y_diff) > 7 || x_diff * x_diff + y_diff * y_diff > 25) return NULL;
-
-		/* crash both trains */
-		tcc->num += TrainCrashed(Train::From(tcc->v));
-		tcc->num += TrainCrashed(Train::From(coll));
-
-		/* Try to reserve all tiles directly under the crashed trains.
-		 * As there might be more than two trains involved, we have to do that for all vehicles */
-		const Train *u;
-		FOR_ALL_TRAINS(u) {
-			if ((u->vehstatus & VS_CRASHED) && u->track != TRACK_BIT_DEPOT) {
-				TrackBits trackbits = u->track;
-				if (trackbits == TRACK_BIT_WORMHOLE) {
-					/* Vehicle is inside a wormhole, v->track contains no useful value then. */
-					trackbits = DiagDirToDiagTrackBits(GetTunnelBridgeDirection(u->tile));
-				}
-				TryReserveRailTrack(u->tile, TrackBitsToTrack(trackbits));
-			}
-		}
-	}
-
-	return NULL;
+	Train *coll = Train::From(v)->First();
+
+	/* can't collide with own wagons */
+	if (coll == tcc->v) return NULL;
+
+	int x_diff = v->x_pos - tcc->v->x_pos;
+	int y_diff = v->y_pos - tcc->v->y_pos;
+
+	/* Do fast calculation to check whether trains are not in close vicinity
+	 * and quickly reject trains distant enough for any collision.
+	 * Differences are shifted by 7, mapping range [-7 .. 8] into [0 .. 15]
+	 * Differences are then ORed and then we check for any higher bits */
+	uint hash = (y_diff + 7) | (x_diff + 7);
+	if (hash & ~15) return NULL;
+
+	/* Slower check using multiplication */
+	if (x_diff * x_diff + y_diff * y_diff > 25) return NULL;
+
+	/* Happens when there is a train under bridge next to bridge head */
+	if (abs(v->z_pos - tcc->v->z_pos) > 5) return NULL;
+
+	/* crash both trains */
+	tcc->num += TrainCrashed(tcc->v);
+	tcc->num += TrainCrashed(coll);
+
+	return NULL; // continue searching
 }
 
 /**