|
@@ -274,49 +274,49 @@ CommandCost CmdBuildRoadVeh(TileIndex ti
|
|
|
v->compatible_roadtypes = RoadTypeToRoadTypes(v->roadtype);
|
|
|
v->rcache.cached_veh_length = 8;
|
|
|
|
|
|
v->vehicle_flags = 0;
|
|
|
if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) SetBit(v->vehicle_flags, VF_BUILT_AS_PROTOTYPE);
|
|
|
|
|
|
v->cargo_cap = rvi->capacity;
|
|
|
|
|
|
AddArticulatedParts(v, VEH_ROAD);
|
|
|
v->InvalidateNewGRFCacheOfChain();
|
|
|
|
|
|
/* Call various callbacks after the whole consist has been constructed */
|
|
|
for (RoadVehicle *u = v; u != NULL; u = u->Next()) {
|
|
|
u->rcache.cached_veh_length = GetRoadVehLength(u);
|
|
|
/* Cargo capacity is zero if and only if the vehicle cannot carry anything */
|
|
|
if (u->cargo_cap != 0) u->cargo_cap = GetVehicleProperty(u, 0x0F, u->cargo_cap);
|
|
|
v->InvalidateNewGRFCache();
|
|
|
u->InvalidateNewGRFCache();
|
|
|
}
|
|
|
|
|
|
VehicleMove(v, false);
|
|
|
|
|
|
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
|
|
|
InvalidateWindowClassesData(WC_ROADVEH_LIST, 0);
|
|
|
InvalidateWindow(WC_COMPANY, v->owner);
|
|
|
SetWindowDirty(WC_COMPANY, v->owner);
|
|
|
if (IsLocalCompany()) {
|
|
|
InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Road window
|
|
|
}
|
|
|
|
|
|
Company::Get(_current_company)->num_engines[p1]++;
|
|
|
|
|
|
CheckConsistencyOfArticulatedVehicle(v);
|
|
|
}
|
|
|
|
|
|
return cost;
|
|
|
}
|
|
|
|
|
|
void ClearSlot(RoadVehicle *v)
|
|
|
{
|
|
|
RoadStop *rs = v->slot;
|
|
|
if (v->slot == NULL) return;
|
|
|
|
|
|
v->slot = NULL;
|
|
|
v->slot_age = 0;
|
|
|
|
|
|
assert(rs->num_vehicles != 0);
|
|
|
rs->num_vehicles--;
|
|
|
|
|
|
DEBUG(ms, 3, "Clearing slot at 0x%X", rs->xy);
|
|
@@ -599,109 +599,109 @@ static Vehicle *EnumCheckRoadVehCrashTra
|
|
|
return
|
|
|
v->type == VEH_TRAIN &&
|
|
|
abs(v->z_pos - u->z_pos) <= 6 &&
|
|
|
abs(v->x_pos - u->x_pos) <= 4 &&
|
|
|
abs(v->y_pos - u->y_pos) <= 4 ?
|
|
|
v : NULL;
|
|
|
}
|
|
|
|
|
|
static void RoadVehCrash(RoadVehicle *v)
|
|
|
{
|
|
|
uint16 pass = 1;
|
|
|
|
|
|
v->crashed_ctr++;
|
|
|
|
|
|
for (Vehicle *u = v; u != NULL; u = u->Next()) {
|
|
|
if (IsCargoInClass(u->cargo_type, CC_PASSENGERS)) pass += u->cargo.Count();
|
|
|
|
|
|
u->vehstatus |= VS_CRASHED;
|
|
|
|
|
|
MarkSingleVehicleDirty(u);
|
|
|
}
|
|
|
|
|
|
ClearSlot(v);
|
|
|
|
|
|
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
|
|
|
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
|
|
|
|
|
|
AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, AIEventVehicleCrashed::CRASH_RV_LEVEL_CROSSING));
|
|
|
|
|
|
SetDParam(0, pass);
|
|
|
AddVehicleNewsItem(
|
|
|
(pass == 1) ?
|
|
|
STR_NEWS_ROAD_VEHICLE_CRASH_DRIVER : STR_NEWS_ROAD_VEHICLE_CRASH,
|
|
|
NS_ACCIDENT,
|
|
|
v->index
|
|
|
);
|
|
|
|
|
|
ModifyStationRatingAround(v->tile, v->owner, -160, 22);
|
|
|
SndPlayVehicleFx(SND_12_EXPLOSION, v);
|
|
|
}
|
|
|
|
|
|
static bool RoadVehCheckTrainCrash(RoadVehicle *v)
|
|
|
{
|
|
|
for (RoadVehicle *u = v; u != NULL; u = u->Next()) {
|
|
|
if (u->state == RVSB_WORMHOLE) continue;
|
|
|
|
|
|
TileIndex tile = u->tile;
|
|
|
|
|
|
if (!IsLevelCrossingTile(tile)) continue;
|
|
|
|
|
|
if (HasVehicleOnPosXY(v->x_pos, v->y_pos, u, EnumCheckRoadVehCrashTrain)) {
|
|
|
RoadVehCrash(v);
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
static void HandleBrokenRoadVeh(RoadVehicle *v)
|
|
|
{
|
|
|
if (v->breakdown_ctr != 1) {
|
|
|
v->breakdown_ctr = 1;
|
|
|
v->cur_speed = 0;
|
|
|
|
|
|
if (v->breakdowns_since_last_service != 255)
|
|
|
v->breakdowns_since_last_service++;
|
|
|
|
|
|
InvalidateWindow(WC_VEHICLE_VIEW, v->index);
|
|
|
InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
|
|
|
SetWindowDirty(WC_VEHICLE_VIEW, v->index);
|
|
|
SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
|
|
|
|
|
|
if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
|
|
|
SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
|
|
|
SND_0F_VEHICLE_BREAKDOWN : SND_35_COMEDY_BREAKDOWN, v);
|
|
|
}
|
|
|
|
|
|
if (!(v->vehstatus & VS_HIDDEN)) {
|
|
|
EffectVehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
|
|
|
if (u != NULL) u->animation_state = v->breakdown_delay * 2;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if ((v->tick_counter & 1) == 0) {
|
|
|
if (--v->breakdown_delay == 0) {
|
|
|
v->breakdown_ctr = 0;
|
|
|
InvalidateWindow(WC_VEHICLE_VIEW, v->index);
|
|
|
SetWindowDirty(WC_VEHICLE_VIEW, v->index);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
TileIndex RoadVehicle::GetOrderStationLocation(StationID station)
|
|
|
{
|
|
|
if (station == this->last_station_visited) this->last_station_visited = INVALID_STATION;
|
|
|
|
|
|
TileIndex dest;
|
|
|
if (YapfFindNearestRoadVehicleCompatibleStop(this, station, &dest)) {
|
|
|
return dest;
|
|
|
} else {
|
|
|
/* There is no stop left at the station, so don't even TRY to go there */
|
|
|
this->IncrementOrderIndex();
|
|
|
return 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
static void StartRoadVehSound(const RoadVehicle *v)
|
|
|
{
|
|
|
if (!PlayVehicleSound(v, VSE_START)) {
|
|
|
SoundID s = RoadVehInfo(v->engine_type)->sfx;
|
|
|
if (s == SND_19_BUS_START_PULL_AWAY && (v->tick_counter & 3) == 0)
|
|
|
s = SND_1A_BUS_START_PULL_AWAY_WITH_HORN;
|
|
@@ -815,49 +815,49 @@ static void RoadVehArrivesAt(const RoadV
|
|
|
|
|
|
static int RoadVehAccelerate(RoadVehicle *v)
|
|
|
{
|
|
|
uint oldspeed = v->cur_speed;
|
|
|
uint accel = 256 + (v->overtaking != 0 ? 256 : 0);
|
|
|
uint spd = v->subspeed + accel;
|
|
|
|
|
|
v->subspeed = (uint8)spd;
|
|
|
|
|
|
int tempmax = v->max_speed;
|
|
|
if (v->cur_speed > v->max_speed) {
|
|
|
tempmax = v->cur_speed - (v->cur_speed / 10) - 1;
|
|
|
}
|
|
|
|
|
|
v->cur_speed = spd = Clamp(v->cur_speed + ((int)spd >> 8), 0, tempmax);
|
|
|
|
|
|
/* Apply bridge speed limit */
|
|
|
if (v->state == RVSB_WORMHOLE && !(v->vehstatus & VS_HIDDEN)) {
|
|
|
v->cur_speed = min(v->cur_speed, GetBridgeSpec(GetBridgeType(v->tile))->speed * 2);
|
|
|
}
|
|
|
|
|
|
/* Update statusbar only if speed has changed to save CPU time */
|
|
|
if (oldspeed != v->cur_speed) {
|
|
|
if (_settings_client.gui.vehicle_speed) {
|
|
|
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
|
|
|
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* Speed is scaled in the same manner as for trains. @see train_cmd.cpp */
|
|
|
int scaled_spd = spd * 3 >> 2;
|
|
|
|
|
|
scaled_spd += v->progress;
|
|
|
v->progress = 0;
|
|
|
return scaled_spd;
|
|
|
}
|
|
|
|
|
|
static Direction RoadVehGetNewDirection(const RoadVehicle *v, int x, int y)
|
|
|
{
|
|
|
static const Direction _roadveh_new_dir[] = {
|
|
|
DIR_N , DIR_NW, DIR_W , INVALID_DIR,
|
|
|
DIR_NE, DIR_N , DIR_SW, INVALID_DIR,
|
|
|
DIR_E , DIR_SE, DIR_S
|
|
|
};
|
|
|
|
|
|
x = x - v->x_pos + 1;
|
|
|
y = y - v->y_pos + 1;
|
|
|
|
|
|
if ((uint)x > 2 || (uint)y > 2) return v->direction;
|
|
|
return _roadveh_new_dir[y * 4 + x];
|
|
@@ -1709,49 +1709,49 @@ again:
|
|
|
} else if (v->slot != NULL) {
|
|
|
/* We are leaving the wrong station
|
|
|
* XXX The question is .. what to do? Actually we shouldn't be here
|
|
|
* but I guess we need to clear the slot */
|
|
|
DEBUG(ms, 0, "Vehicle %d (index %d) arrived at wrong stop", v->unitnumber, v->index);
|
|
|
if (v->tile != v->dest_tile) {
|
|
|
DEBUG(ms, 2, " current tile 0x%X is not destination tile 0x%X. Route problem", v->tile, v->dest_tile);
|
|
|
}
|
|
|
if (v->dest_tile != v->slot->xy) {
|
|
|
DEBUG(ms, 2, " stop tile 0x%X is not destination tile 0x%X. Multistop desync", v->slot->xy, v->dest_tile);
|
|
|
}
|
|
|
if (!v->current_order.IsType(OT_GOTO_STATION)) {
|
|
|
DEBUG(ms, 2, " current order type (%d) is not OT_GOTO_STATION", v->current_order.GetType());
|
|
|
} else {
|
|
|
if (v->current_order.GetDestination() != st->index)
|
|
|
DEBUG(ms, 2, " current station %d is not target station in current_order.station (%d)",
|
|
|
st->index, v->current_order.GetDestination());
|
|
|
}
|
|
|
|
|
|
DEBUG(ms, 2, " force a slot clearing");
|
|
|
ClearSlot(v);
|
|
|
}
|
|
|
|
|
|
StartRoadVehSound(v);
|
|
|
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
|
|
|
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
|
|
|
}
|
|
|
|
|
|
/* Check tile position conditions - i.e. stop position in depot,
|
|
|
* entry onto bridge or into tunnel */
|
|
|
uint32 r = VehicleEnterTile(v, v->tile, x, y);
|
|
|
if (HasBit(r, VETS_CANNOT_ENTER)) {
|
|
|
v->cur_speed = 0;
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
if (v->current_order.IsType(OT_LEAVESTATION) && IsDriveThroughStopTile(v->tile)) {
|
|
|
v->current_order.Free();
|
|
|
ClearSlot(v);
|
|
|
}
|
|
|
|
|
|
/* Move to next frame unless vehicle arrived at a stop position
|
|
|
* in a depot or entered a tunnel/bridge */
|
|
|
if (!HasBit(r, VETS_ENTERED_WORMHOLE)) v->frame++;
|
|
|
|
|
|
RoadZPosAffectSpeed(v, SetRoadVehPosition(v, x, y, true));
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
static bool RoadVehController(RoadVehicle *v)
|
|
@@ -1824,67 +1824,67 @@ bool RoadVehicle::Tick()
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
static void CheckIfRoadVehNeedsService(RoadVehicle *v)
|
|
|
{
|
|
|
static const uint MAX_ACCEPTABLE_DEPOT_DIST = 16;
|
|
|
|
|
|
/* If we already got a slot at a stop, use that FIRST, and go to a depot later */
|
|
|
if (v->slot != NULL || Company::Get(v->owner)->settings.vehicle.servint_roadveh == 0 || !v->NeedsAutomaticServicing()) return;
|
|
|
if (v->IsInDepot()) {
|
|
|
VehicleServiceInDepot(v);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
RoadFindDepotData rfdd = FindClosestRoadDepot(v, MAX_ACCEPTABLE_DEPOT_DIST);
|
|
|
/* Only go to the depot if it is not too far out of our way. */
|
|
|
if (rfdd.best_length == UINT_MAX || rfdd.best_length > MAX_ACCEPTABLE_DEPOT_DIST) {
|
|
|
if (v->current_order.IsType(OT_GOTO_DEPOT)) {
|
|
|
/* If we were already heading for a depot but it has
|
|
|
* suddenly moved farther away, we continue our normal
|
|
|
* schedule? */
|
|
|
v->current_order.MakeDummy();
|
|
|
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
|
|
|
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
|
|
|
}
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
DepotID depot = GetDepotIndex(rfdd.tile);
|
|
|
|
|
|
if (v->current_order.IsType(OT_GOTO_DEPOT) &&
|
|
|
v->current_order.GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS &&
|
|
|
!Chance16(1, 20)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (v->current_order.IsType(OT_LOADING)) v->LeaveStation();
|
|
|
ClearSlot(v);
|
|
|
|
|
|
v->current_order.MakeGoToDepot(depot, ODTFB_SERVICE);
|
|
|
v->dest_tile = rfdd.tile;
|
|
|
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
|
|
|
SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
|
|
|
}
|
|
|
|
|
|
void RoadVehicle::OnNewDay()
|
|
|
{
|
|
|
if (!this->IsRoadVehFront()) return;
|
|
|
|
|
|
if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
|
|
|
if (this->blocked_ctr == 0) CheckVehicleBreakdown(this);
|
|
|
|
|
|
AgeVehicle(this);
|
|
|
CheckIfRoadVehNeedsService(this);
|
|
|
|
|
|
CheckOrders(this);
|
|
|
|
|
|
/* Current slot has expired */
|
|
|
if (this->current_order.IsType(OT_GOTO_STATION) && this->slot != NULL && this->slot_age-- == 0) {
|
|
|
DEBUG(ms, 3, "Slot expired for vehicle %d (index %d) at stop 0x%X",
|
|
|
this->unitnumber, this->index, this->slot->xy);
|
|
|
ClearSlot(this);
|
|
|
}
|
|
|
|
|
|
/* update destination */
|
|
|
if (!(this->vehstatus & VS_STOPPED) && this->current_order.IsType(OT_GOTO_STATION) && !(this->current_order.GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) && this->slot == NULL && !(this->vehstatus & VS_CRASHED)) {
|
|
|
Station *st = Station::Get(this->current_order.GetDestination());
|
|
@@ -1936,50 +1936,50 @@ void RoadVehicle::OnNewDay()
|
|
|
this->dest_tile = best->xy;
|
|
|
this->slot_age = 14;
|
|
|
} else {
|
|
|
DEBUG(ms, 3, "Could not find a suitable stop");
|
|
|
}
|
|
|
} else {
|
|
|
DEBUG(ms, 5, "Distance from station too far. Postponing slotting for vehicle %d (index %d) at station %d, (0x%X)",
|
|
|
this->unitnumber, this->index, st->index, st->xy);
|
|
|
}
|
|
|
} else {
|
|
|
DEBUG(ms, 4, "No road stop for vehicle %d (index %d) at station %d (0x%X)",
|
|
|
this->unitnumber, this->index, st->index, st->xy);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (this->running_ticks == 0) return;
|
|
|
|
|
|
CommandCost cost(EXPENSES_ROADVEH_RUN, this->GetRunningCost() * this->running_ticks / (DAYS_IN_YEAR * DAY_TICKS));
|
|
|
|
|
|
this->profit_this_year -= cost.GetCost();
|
|
|
this->running_ticks = 0;
|
|
|
|
|
|
SubtractMoneyFromCompanyFract(this->owner, cost);
|
|
|
|
|
|
InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
|
|
|
InvalidateWindowClasses(WC_ROADVEH_LIST);
|
|
|
SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
|
|
|
SetWindowClassesDirty(WC_ROADVEH_LIST);
|
|
|
}
|
|
|
|
|
|
Trackdir RoadVehicle::GetVehicleTrackdir() const
|
|
|
{
|
|
|
if (this->vehstatus & VS_CRASHED) return INVALID_TRACKDIR;
|
|
|
|
|
|
if (this->IsInDepot()) {
|
|
|
/* We'll assume the road vehicle is facing outwards */
|
|
|
return DiagDirToDiagTrackdir(GetRoadDepotDirection(this->tile));
|
|
|
}
|
|
|
|
|
|
if (IsStandardRoadStopTile(this->tile)) {
|
|
|
/* We'll assume the road vehicle is facing outwards */
|
|
|
return DiagDirToDiagTrackdir(GetRoadStopDir(this->tile)); // Road vehicle in a station
|
|
|
}
|
|
|
|
|
|
/* Drive through road stops / wormholes (tunnels) */
|
|
|
if (this->state > RVSB_TRACKDIR_MASK) return DiagDirToDiagTrackdir(DirToDiagDir(this->direction));
|
|
|
|
|
|
/* If vehicle's state is a valid track direction (vehicle is not turning around) return it,
|
|
|
* otherwise transform it into a valid track direction */
|
|
|
return (Trackdir)((IsReversingRoadTrackdir((Trackdir)this->state)) ? (this->state - 6) : this->state);
|
|
|
}
|
|
|
|
|
@@ -2048,36 +2048,36 @@ CommandCost CmdRefitRoadVeh(TileIndex ti
|
|
|
case CT_PASSENGERS: break;
|
|
|
case CT_MAIL:
|
|
|
case CT_GOODS: capacity *= 2; break;
|
|
|
default: capacity *= 4; break;
|
|
|
}
|
|
|
switch (new_cid) {
|
|
|
case CT_PASSENGERS: break;
|
|
|
case CT_MAIL:
|
|
|
case CT_GOODS: capacity /= 2; break;
|
|
|
default: capacity /= 4; break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
total_capacity += capacity;
|
|
|
|
|
|
if (new_cid != v->cargo_type) {
|
|
|
cost.AddCost(GetRefitCost(v->engine_type));
|
|
|
}
|
|
|
|
|
|
if (flags & DC_EXEC) {
|
|
|
v->cargo_cap = capacity;
|
|
|
v->cargo.Truncate((v->cargo_type == new_cid) ? capacity : 0);
|
|
|
v->cargo_type = new_cid;
|
|
|
v->cargo_subtype = new_subtype;
|
|
|
InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
|
|
|
InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
|
|
|
SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
|
|
|
SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
|
|
|
InvalidateWindowClassesData(WC_ROADVEH_LIST, 0);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (flags & DC_EXEC) RoadVehUpdateCache(RoadVehicle::Get(p1)->First());
|
|
|
|
|
|
_returned_refit_capacity = total_capacity;
|
|
|
|
|
|
return cost;
|
|
|
}
|