Safer level crossings: add setting to improve RV level crossing safety.

pull/18/head
Jonathan G Rennison 7 years ago
parent 2ee66e9b24
commit 0acb4fdd2f

@ -650,6 +650,7 @@
<li>m5 bit 5: set if crossing lights are on</li>
<li>m7 bits 4..0: <a href="#OwnershipInfo">owner</a> of the road type 0 (normal road)</li>
<li>m5 bit 4: pbs reservation state</li>
<li style="color: blue">m5 bit 1: set if crossing is possibly occupied by a road vehicle</li>
</ul>
</li>
</ul>

@ -150,7 +150,7 @@ the array so you can quickly see what is used and what is not.
<td class="bits">-inherit-</td>
<td class="bits">XXXX XXXX</td>
<td class="bits">-inherit-</td>
<td class="bits">XXXX<span class="free"> OOO</span>X</td>
<td class="bits">XXXX<span class="free"> OO</span><span class="used_p">P</span>X</td>
<td class="bits"><span class="free">OO</span>XX X<span class="free">OOO</span></td>
<td class="bits">XXXX XXXX</td>
</tr>

@ -1850,6 +1850,8 @@ STR_CONFIG_SETTING_QUERY_CAPTION :{WHITE}Change s
STR_CONFIG_SETTING_ADJACENT_CROSSINGS :Close adjacent level crossings: {STRING2}
STR_CONFIG_SETTING_ADJACENT_CROSSINGS_HELPTEXT :Closes all adjacent level crossings on parallel tracks whenever one or more is occupied
STR_CONFIG_SETTING_SAFER_CROSSINGS :Safer level crossings: {STRING2}
STR_CONFIG_SETTING_SAFER_CROSSINGS_HELPTEXT :Changes to level crossings to improve road vehicle safety
STR_CONFIG_SETTING_PAY_FOR_REPAIR_VEHICLE :Pay for repairing vehicles: {STRING2}
STR_CONFIG_SETTING_PAY_FOR_REPAIR_VEHICLE_HELPTEXT :Pay for repairing vehicles which have broken down

@ -125,6 +125,18 @@ bool TryReserveRailTrack(TileIndex tile, Track t, bool trigger_stations)
case MP_ROAD:
if (IsLevelCrossing(tile) && !HasCrossingReservation(tile)) {
if (_settings_game.vehicle.safer_crossings) {
if (IsCrossingOccupiedByRoadVehicle(tile)) return false;
if (_settings_game.vehicle.adjacent_crossings) {
const Axis axis = GetCrossingRoadAxis(tile);
for (TileIndex t = tile; IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, AxisToDiagDir(GetCrossingRoadAxis(t)))) {
if (IsCrossingOccupiedByRoadVehicle(t)) return false;
}
for (TileIndex t = tile; IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis; t = TileAddByDiagDir(t, ReverseDiagDir(AxisToDiagDir(GetCrossingRoadAxis(t))))) {
if (IsCrossingOccupiedByRoadVehicle(t)) return false;
}
}
}
SetCrossingReservation(tile, true);
UpdateLevelCrossing(tile, false);
return true;

@ -1887,6 +1887,12 @@ static VehicleEnterTileStatus VehicleEnter_Road(Vehicle *v, TileIndex tile, int
break;
}
case ROAD_TILE_CROSSING: {
if (v->type != VEH_ROAD) break;
SetCrossingOccupiedByRoadVehicle(tile, true);
break;
}
default: break;
}
return VETSB_CONTINUE;

@ -180,5 +180,6 @@ bool ValParamRoadType(const RoadType rt);
RoadTypes GetCompanyRoadtypes(const CompanyID company);
void UpdateLevelCrossing(TileIndex tile, bool sound = true);
bool IsCrossingOccupiedByRoadVehicle(TileIndex t);
#endif /* ROAD_FUNC_H */

@ -402,21 +402,27 @@ static inline void SetCrossingBarred(TileIndex t, bool barred)
}
/**
* Unbar a level crossing.
* @param t The tile to change.
* Check if the level crossing is possibly occupied by road vehicle(s).
* @param t The tile to query.
* @pre IsLevelCrossing(t)
* @return True if the level crossing is marked as occupied. This may return false positives.
*/
static inline void UnbarCrossing(TileIndex t)
static inline bool IsCrossingPossiblyOccupiedByRoadVehicle(TileIndex t)
{
SetCrossingBarred(t, false);
assert(IsLevelCrossing(t));
return HasBit(_m[t].m5, 1);
}
/**
* Bar a level crossing.
* @param t The tile to change.
* Set whether the level crossing is occupied by road vehicle(s).
* @param t The tile to modify.
* @param barred True if the crossing should be marked as occupied, false otherwise.
* @pre IsLevelCrossing(t)
*/
static inline void BarCrossing(TileIndex t)
static inline void SetCrossingOccupiedByRoadVehicle(TileIndex t, bool occupied)
{
SetCrossingBarred(t, true);
assert(IsLevelCrossing(t));
SB(_m[t].m5, 1, 1, occupied ? 1 : 0);
}
/** Check if a road tile has snow/desert. */

@ -3386,6 +3386,14 @@ bool AfterLoadGame()
_settings_game.economy.day_length_factor = 1;
}
if (SlXvIsFeatureMissing(XSLFI_SAFER_CROSSINGS)) {
for (TileIndex t = 0; t < map_size; t++) {
if (IsLevelCrossingTile(t)) {
SetCrossingOccupiedByRoadVehicle(t, EnsureNoRoadVehicleOnGround(t).Failed());
}
}
}
/* Road stops is 'only' updating some caches */
AfterLoadRoadStops();
AfterLoadLabelMaps();

@ -51,6 +51,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_TRACE_RESTRICT_OWNER, XSCF_NULL, 1, 1, "tracerestrict_owner", NULL, NULL, NULL },
{ XSLFI_PROG_SIGS, XSCF_NULL, 1, 1, "programmable_signals", NULL, NULL, "SPRG" },
{ XSLFI_ADJACENT_CROSSINGS, XSCF_NULL, 1, 1, "adjacent_crossings", NULL, NULL, NULL },
{ XSLFI_SAFER_CROSSINGS, XSCF_NULL, 1, 1, "safer_crossings", NULL, NULL, NULL },
{ XSLFI_DEPARTURE_BOARDS, XSCF_IGNORABLE_UNKNOWN, 1, 1, "departure_boards", NULL, NULL, NULL },
{ XSLFI_TIMETABLES_START_TICKS, XSCF_NULL, 2, 2, "timetable_start_ticks", NULL, NULL, NULL },
{ XSLFI_TOWN_CARGO_ADJ, XSCF_IGNORABLE_UNKNOWN, 2, 2, "town_cargo_adj", NULL, NULL, NULL },

@ -25,6 +25,7 @@ enum SlXvFeatureIndex {
XSLFI_TRACE_RESTRICT_OWNER, ///< Trace restrict: train owner test
XSLFI_PROG_SIGS, ///< programmable signals patch
XSLFI_ADJACENT_CROSSINGS, ///< Adjacent level crossings closure patch
XSLFI_SAFER_CROSSINGS, ///< Safer level crossings
XSLFI_DEPARTURE_BOARDS, ///< Departure boards patch, in ticks mode
XSLFI_TIMETABLES_START_TICKS, ///< Timetable start time is in ticks, instead of days (from departure boards patch)
XSLFI_TOWN_CARGO_ADJ, ///< Town cargo adjustment patch

@ -1694,6 +1694,7 @@ static SettingsContainer &GetSettingsTree()
vehicles->Add(new SettingEntry("order.no_servicing_if_no_breakdowns"));
vehicles->Add(new SettingEntry("order.serviceathelipad"));
vehicles->Add(new SettingEntry("vehicle.adjacent_crossings"));
vehicles->Add(new SettingEntry("vehicle.safer_crossings"));
}
SettingsPage *limitations = main->Add(new SettingsPage(STR_CONFIG_SETTING_LIMITATIONS));

@ -515,6 +515,7 @@ struct VehicleSettings {
byte road_side; ///< the side of the road vehicles drive on
uint8 plane_crashes; ///< number of plane crashes, 0 = none, 1 = reduced, 2 = normal
bool adjacent_crossings; ///< enable closing of adjacent level crossings
bool safer_crossings; ///< enable safer level crossings
bool improved_breakdowns; ///< different types, chances and severities of breakdowns
bool pay_for_repair; ///< pay for repairing vehicle
uint8 repair_cost; ///< cost of repairing vehicle

@ -400,6 +400,7 @@ static SigInfo ExploreSegment(Owner owner)
if (DiagDirToAxis(enterdir) == GetCrossingRoadAxis(tile)) continue; // different axis
if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) info.flags |= SF_TRAIN;
if (_settings_game.vehicle.safer_crossings) info.flags |= SF_PBS;
tile += TileOffsByDiagDir(exitdir);
break;

@ -3057,6 +3057,15 @@ strhelp = STR_CONFIG_SETTING_ADJACENT_CROSSINGS_HELPTEXT
cat = SC_BASIC
patxname = ""adjacent_crossings.vehicle.adjacent_crossings""
[SDT_BOOL]
base = GameSettings
var = vehicle.safer_crossings
def = false
str = STR_CONFIG_SETTING_SAFER_CROSSINGS
strhelp = STR_CONFIG_SETTING_SAFER_CROSSINGS_HELPTEXT
cat = SC_BASIC
patxname = ""safer_crossings.vehicle.safer_crossings""
;***************************************************************************
; Unsaved setting variables.

@ -1866,6 +1866,20 @@ void UpdateLevelCrossing(TileIndex tile, bool sound)
}
}
/**
* Check if the level crossing is occupied by road vehicle(s).
* @param t The tile to query.
* @pre IsLevelCrossing(t)
* @return True if the level crossing is marked as occupied.
*/
bool IsCrossingOccupiedByRoadVehicle(TileIndex t)
{
if (!IsCrossingPossiblyOccupiedByRoadVehicle(t)) return false;
const bool occupied = EnsureNoRoadVehicleOnGround(t).Failed();
SetCrossingOccupiedByRoadVehicle(t, occupied);
return occupied;
}
/**
* Bars crossing and plays ding-ding sound if not barred already

@ -575,6 +575,40 @@ CommandCost EnsureNoVehicleOnGround(TileIndex tile)
return CommandCost();
}
/**
* Callback that returns 'real' vehicles lower or at height \c *(int*)data, for road vehicles.
* @param v Vehicle to examine.
* @param data Pointer to height data.
* @return \a v if conditions are met, else \c NULL.
*/
static Vehicle *EnsureNoRoadVehicleProcZ(Vehicle *v, void *data)
{
int z = *(int*)data;
if (v->type != VEH_ROAD) return NULL;
if (v->z_pos > z) return NULL;
return v;
}
/**
* Ensure there is no road vehicle at the ground at the given position.
* @param tile Position to examine.
* @return Succeeded command (ground is free) or failed command (a vehicle is found).
*/
CommandCost EnsureNoRoadVehicleOnGround(TileIndex tile)
{
int z = GetTileMaxPixelZ(tile);
/* Value v is not safe in MP games, however, it is used to generate a local
* error message only (which may be different for different machines).
* Such a message does not affect MP synchronisation.
*/
Vehicle *v = VehicleFromPos(tile, &z, &EnsureNoRoadVehicleProcZ, true);
if (v != NULL) return_cmd_error(STR_ERROR_ROAD_VEHICLE_IN_THE_WAY);
return CommandCost();
}
/** Procedure called for every vehicle found in tunnel/bridge in the hash map */
static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
{

@ -165,6 +165,7 @@ static inline uint32 GetCmdSendToDepot(const BaseVehicle *v)
}
CommandCost EnsureNoVehicleOnGround(TileIndex tile);
CommandCost EnsureNoRoadVehicleOnGround(TileIndex tile);
CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits);
extern VehicleID _new_vehicle_id;

Loading…
Cancel
Save