Changeset - r20480:93a50078f0bb
[Not reviewed]
master
0 1 0
rubidium - 11 years ago 2013-06-28 18:09:07
rubidium@openttd.org
(svn r25495) -Fix [FS#5553]: when addings bits to a (train) station, the train trying to stop there could overshoot the (new) stop location and not stop at all
1 file changed with 3 insertions and 6 deletions:
0 comments (0 inline, 0 general)
src/station_cmd.cpp
Show inline comments
 
@@ -2935,405 +2935,402 @@ static int GetSlopePixelZ_Station(TileIn
 
static Foundation GetFoundation_Station(TileIndex tile, Slope tileh)
 
{
 
	return FlatteningFoundation(tileh);
 
}
 

	
 
static void GetTileDesc_Station(TileIndex tile, TileDesc *td)
 
{
 
	td->owner[0] = GetTileOwner(tile);
 
	if (IsDriveThroughStopTile(tile)) {
 
		Owner road_owner = INVALID_OWNER;
 
		Owner tram_owner = INVALID_OWNER;
 
		RoadTypes rts = GetRoadTypes(tile);
 
		if (HasBit(rts, ROADTYPE_ROAD)) road_owner = GetRoadOwner(tile, ROADTYPE_ROAD);
 
		if (HasBit(rts, ROADTYPE_TRAM)) tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM);
 

	
 
		/* Is there a mix of owners? */
 
		if ((tram_owner != INVALID_OWNER && tram_owner != td->owner[0]) ||
 
				(road_owner != INVALID_OWNER && road_owner != td->owner[0])) {
 
			uint i = 1;
 
			if (road_owner != INVALID_OWNER) {
 
				td->owner_type[i] = STR_LAND_AREA_INFORMATION_ROAD_OWNER;
 
				td->owner[i] = road_owner;
 
				i++;
 
			}
 
			if (tram_owner != INVALID_OWNER) {
 
				td->owner_type[i] = STR_LAND_AREA_INFORMATION_TRAM_OWNER;
 
				td->owner[i] = tram_owner;
 
			}
 
		}
 
	}
 
	td->build_date = BaseStation::GetByTile(tile)->build_date;
 

	
 
	if (HasStationTileRail(tile)) {
 
		const StationSpec *spec = GetStationSpec(tile);
 

	
 
		if (spec != NULL) {
 
			td->station_class = StationClass::Get(spec->cls_id)->name;
 
			td->station_name  = spec->name;
 

	
 
			if (spec->grf_prop.grffile != NULL) {
 
				const GRFConfig *gc = GetGRFConfig(spec->grf_prop.grffile->grfid);
 
				td->grf = gc->GetName();
 
			}
 
		}
 

	
 
		const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
 
		td->rail_speed = rti->max_speed;
 
	}
 

	
 
	if (IsAirport(tile)) {
 
		const AirportSpec *as = Station::GetByTile(tile)->airport.GetSpec();
 
		td->airport_class = AirportClass::Get(as->cls_id)->name;
 
		td->airport_name = as->name;
 

	
 
		const AirportTileSpec *ats = AirportTileSpec::GetByTile(tile);
 
		td->airport_tile_name = ats->name;
 

	
 
		if (as->grf_prop.grffile != NULL) {
 
			const GRFConfig *gc = GetGRFConfig(as->grf_prop.grffile->grfid);
 
			td->grf = gc->GetName();
 
		} else if (ats->grf_prop.grffile != NULL) {
 
			const GRFConfig *gc = GetGRFConfig(ats->grf_prop.grffile->grfid);
 
			td->grf = gc->GetName();
 
		}
 
	}
 

	
 
	StringID str;
 
	switch (GetStationType(tile)) {
 
		default: NOT_REACHED();
 
		case STATION_RAIL:     str = STR_LAI_STATION_DESCRIPTION_RAILROAD_STATION; break;
 
		case STATION_AIRPORT:
 
			str = (IsHangar(tile) ? STR_LAI_STATION_DESCRIPTION_AIRCRAFT_HANGAR : STR_LAI_STATION_DESCRIPTION_AIRPORT);
 
			break;
 
		case STATION_TRUCK:    str = STR_LAI_STATION_DESCRIPTION_TRUCK_LOADING_AREA; break;
 
		case STATION_BUS:      str = STR_LAI_STATION_DESCRIPTION_BUS_STATION; break;
 
		case STATION_OILRIG:   str = STR_INDUSTRY_NAME_OIL_RIG; break;
 
		case STATION_DOCK:     str = STR_LAI_STATION_DESCRIPTION_SHIP_DOCK; break;
 
		case STATION_BUOY:     str = STR_LAI_STATION_DESCRIPTION_BUOY; break;
 
		case STATION_WAYPOINT: str = STR_LAI_STATION_DESCRIPTION_WAYPOINT; break;
 
	}
 
	td->str = str;
 
}
 

	
 

	
 
static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
 
{
 
	TrackBits trackbits = TRACK_BIT_NONE;
 

	
 
	switch (mode) {
 
		case TRANSPORT_RAIL:
 
			if (HasStationRail(tile) && !IsStationTileBlocked(tile)) {
 
				trackbits = TrackToTrackBits(GetRailStationTrack(tile));
 
			}
 
			break;
 

	
 
		case TRANSPORT_WATER:
 
			/* buoy is coded as a station, it is always on open water */
 
			if (IsBuoy(tile)) {
 
				trackbits = TRACK_BIT_ALL;
 
				/* remove tracks that connect NE map edge */
 
				if (TileX(tile) == 0) trackbits &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
 
				/* remove tracks that connect NW map edge */
 
				if (TileY(tile) == 0) trackbits &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
 
			}
 
			break;
 

	
 
		case TRANSPORT_ROAD:
 
			if ((GetRoadTypes(tile) & sub_mode) != 0 && IsRoadStop(tile)) {
 
				DiagDirection dir = GetRoadStopDir(tile);
 
				Axis axis = DiagDirToAxis(dir);
 

	
 
				if (side != INVALID_DIAGDIR) {
 
					if (axis != DiagDirToAxis(side) || (IsStandardRoadStopTile(tile) && dir != side)) break;
 
				}
 

	
 
				trackbits = AxisToTrackBits(axis);
 
			}
 
			break;
 

	
 
		default:
 
			break;
 
	}
 

	
 
	return CombineTrackStatus(TrackBitsToTrackdirBits(trackbits), TRACKDIR_BIT_NONE);
 
}
 

	
 

	
 
static void TileLoop_Station(TileIndex tile)
 
{
 
	/* FIXME -- GetTileTrackStatus_Station -> animated stationtiles
 
	 * hardcoded.....not good */
 
	switch (GetStationType(tile)) {
 
		case STATION_AIRPORT:
 
			AirportTileAnimationTrigger(Station::GetByTile(tile), tile, AAT_TILELOOP);
 
			break;
 

	
 
		case STATION_DOCK:
 
			if (GetTileSlope(tile) != SLOPE_FLAT) break; // only handle water part
 
			/* FALL THROUGH */
 
		case STATION_OILRIG: //(station part)
 
		case STATION_BUOY:
 
			TileLoop_Water(tile);
 
			break;
 

	
 
		default: break;
 
	}
 
}
 

	
 

	
 
static void AnimateTile_Station(TileIndex tile)
 
{
 
	if (HasStationRail(tile)) {
 
		AnimateStationTile(tile);
 
		return;
 
	}
 

	
 
	if (IsAirport(tile)) {
 
		AnimateAirportTile(tile);
 
	}
 
}
 

	
 

	
 
static bool ClickTile_Station(TileIndex tile)
 
{
 
	const BaseStation *bst = BaseStation::GetByTile(tile);
 

	
 
	if (bst->facilities & FACIL_WAYPOINT) {
 
		ShowWaypointWindow(Waypoint::From(bst));
 
	} else if (IsHangar(tile)) {
 
		const Station *st = Station::From(bst);
 
		ShowDepotWindow(st->airport.GetHangarTile(st->airport.GetHangarNum(tile)), VEH_AIRCRAFT);
 
	} else {
 
		ShowStationViewWindow(bst->index);
 
	}
 
	return true;
 
}
 

	
 
static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, int x, int y)
 
{
 
	if (v->type == VEH_TRAIN) {
 
		StationID station_id = GetStationIndex(tile);
 
		if (!v->current_order.ShouldStopAtStation(v, station_id)) return VETSB_CONTINUE;
 
		if (!IsRailStation(tile) || !v->IsFrontEngine()) return VETSB_CONTINUE;
 

	
 
		int station_ahead;
 
		int station_length;
 
		int stop = GetTrainStopLocation(station_id, tile, Train::From(v), &station_ahead, &station_length);
 

	
 
		/* Stop whenever that amount of station ahead + the distance from the
 
		 * begin of the platform to the stop location is longer than the length
 
		 * of the platform. Station ahead 'includes' the current tile where the
 
		 * vehicle is on, so we need to subtract that. */
 
		if (!IsInsideBS(stop + station_ahead, station_length, TILE_SIZE)) return VETSB_CONTINUE;
 
		if (stop + station_ahead - (int)TILE_SIZE >= station_length) return VETSB_CONTINUE;
 

	
 
		DiagDirection dir = DirToDiagDir(v->direction);
 

	
 
		x &= 0xF;
 
		y &= 0xF;
 

	
 
		if (DiagDirToAxis(dir) != AXIS_X) Swap(x, y);
 
		if (y == TILE_SIZE / 2) {
 
			if (dir != DIAGDIR_SE && dir != DIAGDIR_SW) x = TILE_SIZE - 1 - x;
 
			stop &= TILE_SIZE - 1;
 

	
 
			if (x == stop) return VETSB_ENTERED_STATION | (VehicleEnterTileStatus)(station_id << VETS_STATION_ID_OFFSET); // enter station
 
			if (x < stop) {
 
				uint16 spd;
 
			if (x >= stop) return VETSB_ENTERED_STATION | (VehicleEnterTileStatus)(station_id << VETS_STATION_ID_OFFSET); // enter station
 

	
 
				v->vehstatus |= VS_TRAIN_SLOWING;
 
				spd = max(0, (stop - x) * 20 - 15);
 
			uint16 spd = max(0, (stop - x) * 20 - 15);
 
				if (spd < v->cur_speed) v->cur_speed = spd;
 
			}
 
		}
 
	} else if (v->type == VEH_ROAD) {
 
		RoadVehicle *rv = RoadVehicle::From(v);
 
		if (rv->state < RVSB_IN_ROAD_STOP && !IsReversingRoadTrackdir((Trackdir)rv->state) && rv->frame == 0) {
 
			if (IsRoadStop(tile) && rv->IsFrontEngine()) {
 
				/* Attempt to allocate a parking bay in a road stop */
 
				return RoadStop::GetByTile(tile, GetRoadStopType(tile))->Enter(rv) ? VETSB_CONTINUE : VETSB_CANNOT_ENTER;
 
			}
 
		}
 
	}
 

	
 
	return VETSB_CONTINUE;
 
}
 

	
 
/**
 
 * Run the watched cargo callback for all houses in the catchment area.
 
 * @param st Station.
 
 */
 
void TriggerWatchedCargoCallbacks(Station *st)
 
{
 
	/* Collect cargoes accepted since the last big tick. */
 
	uint cargoes = 0;
 
	for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
 
		if (HasBit(st->goods[cid].acceptance_pickup, GoodsEntry::GES_ACCEPTED_BIGTICK)) SetBit(cargoes, cid);
 
	}
 

	
 
	/* Anything to do? */
 
	if (cargoes == 0) return;
 

	
 
	/* Loop over all houses in the catchment. */
 
	Rect r = st->GetCatchmentRect();
 
	TileArea ta(TileXY(r.left, r.top), TileXY(r.right, r.bottom));
 
	TILE_AREA_LOOP(tile, ta) {
 
		if (IsTileType(tile, MP_HOUSE)) {
 
			WatchedCargoCallback(tile, cargoes);
 
		}
 
	}
 
}
 

	
 
/**
 
 * This function is called for each station once every 250 ticks.
 
 * Not all stations will get the tick at the same time.
 
 * @param st the station receiving the tick.
 
 * @return true if the station is still valid (wasn't deleted)
 
 */
 
static bool StationHandleBigTick(BaseStation *st)
 
{
 
	if (!st->IsInUse()) {
 
		if (++st->delete_ctr >= 8) delete st;
 
		return false;
 
	}
 

	
 
	if (Station::IsExpected(st)) {
 
		TriggerWatchedCargoCallbacks(Station::From(st));
 

	
 
		for (CargoID i = 0; i < NUM_CARGO; i++) {
 
			ClrBit(Station::From(st)->goods[i].acceptance_pickup, GoodsEntry::GES_ACCEPTED_BIGTICK);
 
		}
 
	}
 

	
 

	
 
	if ((st->facilities & FACIL_WAYPOINT) == 0) UpdateStationAcceptance(Station::From(st), true);
 

	
 
	return true;
 
}
 

	
 
static inline void byte_inc_sat(byte *p)
 
{
 
	byte b = *p + 1;
 
	if (b != 0) *p = b;
 
}
 

	
 
static void UpdateStationRating(Station *st)
 
{
 
	bool waiting_changed = false;
 

	
 
	byte_inc_sat(&st->time_since_load);
 
	byte_inc_sat(&st->time_since_unload);
 

	
 
	const CargoSpec *cs;
 
	FOR_ALL_CARGOSPECS(cs) {
 
		GoodsEntry *ge = &st->goods[cs->Index()];
 
		/* Slowly increase the rating back to his original level in the case we
 
		 *  didn't deliver cargo yet to this station. This happens when a bribe
 
		 *  failed while you didn't moved that cargo yet to a station. */
 
		if (!ge->HasRating() && ge->rating < INITIAL_STATION_RATING) {
 
			ge->rating++;
 
		}
 

	
 
		/* Only change the rating if we are moving this cargo */
 
		if (ge->HasRating()) {
 
			byte_inc_sat(&ge->time_since_pickup);
 

	
 
			bool skip = false;
 
			int rating = 0;
 
			uint waiting = ge->cargo.TotalCount();
 

	
 
			/* num_dests is at least 1 if there is any cargo as
 
			 * INVALID_STATION is also a destination.
 
			 */
 
			uint num_dests = (uint)ge->cargo.Packets()->MapSize();
 

	
 
			/* Average amount of cargo per next hop, but prefer solitary stations
 
			 * with only one or two next hops. They are allowed to have more
 
			 * cargo waiting per next hop.
 
			 * With manual cargo distribution waiting_avg = waiting / 2 as then
 
			 * INVALID_STATION is the only destination.
 
			 */
 
			uint waiting_avg = waiting / (num_dests + 1);
 

	
 
			if (HasBit(cs->callback_mask, CBM_CARGO_STATION_RATING_CALC)) {
 
				/* Perform custom station rating. If it succeeds the speed, days in transit and
 
				 * waiting cargo ratings must not be executed. */
 

	
 
				/* NewGRFs expect last speed to be 0xFF when no vehicle has arrived yet. */
 
				uint last_speed = ge->HasVehicleEverTriedLoading() ? ge->last_speed : 0xFF;
 

	
 
				uint32 var18 = min(ge->time_since_pickup, 0xFF) | (min(ge->max_waiting_cargo, 0xFFFF) << 8) | (min(last_speed, 0xFF) << 24);
 
				/* Convert to the 'old' vehicle types */
 
				uint32 var10 = (st->last_vehicle_type == VEH_INVALID) ? 0x0 : (st->last_vehicle_type + 0x10);
 
				uint16 callback = GetCargoCallback(CBID_CARGO_STATION_RATING_CALC, var10, var18, cs);
 
				if (callback != CALLBACK_FAILED) {
 
					skip = true;
 
					rating = GB(callback, 0, 14);
 

	
 
					/* Simulate a 15 bit signed value */
 
					if (HasBit(callback, 14)) rating -= 0x4000;
 
				}
 
			}
 

	
 
			if (!skip) {
 
				int b = ge->last_speed - 85;
 
				if (b >= 0) rating += b >> 2;
 

	
 
				byte waittime = ge->time_since_pickup;
 
				if (st->last_vehicle_type == VEH_SHIP) waittime >>= 2;
 
				(waittime > 21) ||
 
				(rating += 25, waittime > 12) ||
 
				(rating += 25, waittime > 6) ||
 
				(rating += 45, waittime > 3) ||
 
				(rating += 35, true);
 

	
 
				(rating -= 90, ge->max_waiting_cargo > 1500) ||
 
				(rating += 55, ge->max_waiting_cargo > 1000) ||
 
				(rating += 35, ge->max_waiting_cargo > 600) ||
 
				(rating += 10, ge->max_waiting_cargo > 300) ||
 
				(rating += 20, ge->max_waiting_cargo > 100) ||
 
				(rating += 10, true);
 
			}
 

	
 
			if (Company::IsValidID(st->owner) && HasBit(st->town->statues, st->owner)) rating += 26;
 

	
 
			byte age = ge->last_age;
 
			(age >= 3) ||
 
			(rating += 10, age >= 2) ||
 
			(rating += 10, age >= 1) ||
 
			(rating += 13, true);
 

	
 
			{
 
				int or_ = ge->rating; // old rating
 

	
 
				/* only modify rating in steps of -2, -1, 0, 1 or 2 */
 
				ge->rating = rating = or_ + Clamp(Clamp(rating, 0, 255) - or_, -2, 2);
 

	
 
				/* if rating is <= 64 and more than 100 items waiting on average per destination,
 
				 * remove some random amount of goods from the station */
 
				if (rating <= 64 && waiting_avg >= 100) {
 
					int dec = Random() & 0x1F;
 
					if (waiting_avg < 200) dec &= 7;
 
					waiting -= (dec + 1) * num_dests;
 
					waiting_changed = true;
 
				}
 

	
 
				/* if rating is <= 127 and there are any items waiting, maybe remove some goods. */
 
				if (rating <= 127 && waiting != 0) {
 
					uint32 r = Random();
 
					if (rating <= (int)GB(r, 0, 7)) {
 
						/* Need to have int, otherwise it will just overflow etc. */
 
						waiting = max((int)waiting - (int)((GB(r, 8, 2) - 1) * num_dests), 0);
 
						waiting_changed = true;
 
					}
 
				}
 

	
 
				/* At some point we really must cap the cargo. Previously this
 
				 * was a strict 4095, but now we'll have a less strict, but
 
				 * increasingly aggressive truncation of the amount of cargo. */
 
				static const uint WAITING_CARGO_THRESHOLD  = 1 << 12;
 
				static const uint WAITING_CARGO_CUT_FACTOR = 1 <<  6;
 
				static const uint MAX_WAITING_CARGO        = 1 << 15;
 

	
 
				if (waiting > WAITING_CARGO_THRESHOLD) {
 
					uint difference = waiting - WAITING_CARGO_THRESHOLD;
 
					waiting -= (difference / WAITING_CARGO_CUT_FACTOR);
0 comments (0 inline, 0 general)