Tracerestrict: Add reverse at PBS signal sub-action

pull/736/head
Jonathan G Rennison 4 weeks ago
parent 0c3d1e1aac
commit 5b9ed03823

@ -1025,6 +1025,8 @@ STR_TRACE_RESTRICT_TRAIN_STATUS_REQUIRES_SERVICE :requires servic
STR_TRACE_RESTRICT_TRAIN_STATUS_STOPPING_AT_STATION_WAYPOINT :stopping at station/waypoint STR_TRACE_RESTRICT_TRAIN_STATUS_STOPPING_AT_STATION_WAYPOINT :stopping at station/waypoint
STR_TRACE_RESTRICT_REVERSE_SIG :Reverse behind signal STR_TRACE_RESTRICT_REVERSE_SIG :Reverse behind signal
STR_TRACE_RESTRICT_REVERSE_SIG_CANCEL :Cancel reverse behind signal STR_TRACE_RESTRICT_REVERSE_SIG_CANCEL :Cancel reverse behind signal
STR_TRACE_RESTRICT_REVERSE_AT_SIG :Reverse at PBS signal
STR_TRACE_RESTRICT_REVERSE_AT_SIG_CANCEL :Cancel reverse at PBS signal
STR_TRACE_RESTRICT_SET_SPEED_RESTRICTION :Restrict train speed to: {VELOCITY} STR_TRACE_RESTRICT_SET_SPEED_RESTRICTION :Restrict train speed to: {VELOCITY}
STR_TRACE_RESTRICT_REMOVE_SPEED_RESTRICTION :Remove train speed restriction STR_TRACE_RESTRICT_REMOVE_SPEED_RESTRICTION :Remove train speed restriction
STR_TRACE_RESTRICT_TRAIN_NOT_STUCK :Train is not stuck, do not show news reports about waiting at this PBS signal STR_TRACE_RESTRICT_TRAIN_NOT_STUCK :Train is not stuck, do not show news reports about waiting at this PBS signal

@ -80,7 +80,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_TRACE_RESTRICT_OWNER, XSCF_NULL, 1, 1, "tracerestrict_owner", nullptr, nullptr, nullptr }, { XSLFI_TRACE_RESTRICT_OWNER, XSCF_NULL, 1, 1, "tracerestrict_owner", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_ORDRCND, XSCF_NULL, 4, 4, "tracerestrict_order_cond", nullptr, nullptr, nullptr }, { XSLFI_TRACE_RESTRICT_ORDRCND, XSCF_NULL, 4, 4, "tracerestrict_order_cond", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_STATUSCND, XSCF_NULL, 2, 2, "tracerestrict_status_cond", nullptr, nullptr, nullptr }, { XSLFI_TRACE_RESTRICT_STATUSCND, XSCF_NULL, 2, 2, "tracerestrict_status_cond", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_REVERSE, XSCF_NULL, 1, 1, "tracerestrict_reverse", nullptr, nullptr, nullptr }, { XSLFI_TRACE_RESTRICT_REVERSE, XSCF_NULL, 2, 2, "tracerestrict_reverse", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_NEWSCTRL, XSCF_NULL, 1, 1, "tracerestrict_newsctrl", nullptr, nullptr, nullptr }, { XSLFI_TRACE_RESTRICT_NEWSCTRL, XSCF_NULL, 1, 1, "tracerestrict_newsctrl", nullptr, nullptr, nullptr },
{ XSLFI_TRACE_RESTRICT_COUNTER, XSCF_NULL, 1, 1, "tracerestrict_counter", nullptr, nullptr, "TRRC" }, { XSLFI_TRACE_RESTRICT_COUNTER, XSCF_NULL, 1, 1, "tracerestrict_counter", nullptr, nullptr, "TRRC" },
{ XSLFI_TRACE_RESTRICT_TIMEDATE, XSCF_NULL, 2, 2, "tracerestrict_timedate", nullptr, nullptr, nullptr }, { XSLFI_TRACE_RESTRICT_TIMEDATE, XSCF_NULL, 2, 2, "tracerestrict_timedate", nullptr, nullptr, nullptr },

@ -2279,6 +2279,7 @@ class NIHTraceRestrict : public NIHelper {
CA(RESERVE_THROUGH_ALWAYS) CA(RESERVE_THROUGH_ALWAYS)
CA(CMB_SIGNAL_MODE_CTRL) CA(CMB_SIGNAL_MODE_CTRL)
CA(ORDER_CONDITIONALS) CA(ORDER_CONDITIONALS)
CA(REVERSE_AT)
#undef CA #undef CA
output.print(""); output.print("");

@ -804,6 +804,13 @@ void TraceRestrictProgram::Execute(const Train* v, const TraceRestrictProgramInp
case TRRVF_CANCEL_REVERSE_BEHIND: case TRRVF_CANCEL_REVERSE_BEHIND:
out.flags &= ~TRPRF_REVERSE_BEHIND; out.flags &= ~TRPRF_REVERSE_BEHIND;
break; break;
case TRRVF_REVERSE_AT:
out.flags |= TRPRF_REVERSE_AT;
break;
case TRRVF_CANCEL_REVERSE_AT:
out.flags &= ~TRPRF_REVERSE_AT;
break; break;
default: default:
@ -1410,6 +1417,14 @@ CommandCost TraceRestrictProgram::Validate(const std::vector<TraceRestrictItem>
if (condstack.empty()) actions_used_flags &= ~TRPAUF_REVERSE_BEHIND; if (condstack.empty()) actions_used_flags &= ~TRPAUF_REVERSE_BEHIND;
break; break;
case TRRVF_REVERSE_AT:
actions_used_flags |= TRPAUF_REVERSE_AT;
break;
case TRRVF_CANCEL_REVERSE_AT:
if (condstack.empty()) actions_used_flags &= ~TRPAUF_REVERSE_AT;
break;
default: default:
return unknown_instruction(); return unknown_instruction();
} }

@ -174,7 +174,7 @@ enum TraceRestrictItemType : uint8_t {
TRIT_COND_RESERVATION_THROUGH = 32, ///< Test if train reservation passes through tile TRIT_COND_RESERVATION_THROUGH = 32, ///< Test if train reservation passes through tile
TRIT_COND_END = 48, ///< End (exclusive) of conditional item types, note that this has the same value as TRIT_REVERSE TRIT_COND_END = 48, ///< End (exclusive) of conditional item types, note that this has the same value as TRIT_REVERSE
TRIT_REVERSE = 48, ///< Reverse behind signal TRIT_REVERSE = 48, ///< Reverse behind/at signal
TRIT_SPEED_RESTRICTION = 49, ///< Speed restriction TRIT_SPEED_RESTRICTION = 49, ///< Speed restriction
TRIT_NEWS_CONTROL = 50, ///< News control TRIT_NEWS_CONTROL = 50, ///< News control
TRIT_COUNTER = 51, ///< Change counter value TRIT_COUNTER = 51, ///< Change counter value
@ -312,6 +312,8 @@ enum TraceRestrictWaitAtPbsValueField : uint8_t {
enum TraceRestrictReverseValueField : uint8_t { enum TraceRestrictReverseValueField : uint8_t {
TRRVF_REVERSE_BEHIND = 0, ///< Reverse behind signal TRRVF_REVERSE_BEHIND = 0, ///< Reverse behind signal
TRRVF_CANCEL_REVERSE_BEHIND = 1, ///< Cancel reverse behind signal TRRVF_CANCEL_REVERSE_BEHIND = 1, ///< Cancel reverse behind signal
TRRVF_REVERSE_AT = 2, ///< Reverse at PBS signal
TRRVF_CANCEL_REVERSE_AT = 3, ///< Cancel reverse at PBS signal
}; };
/** /**
@ -448,6 +450,7 @@ enum TraceRestrictProgramResultFlags : uint16_t {
TRPRF_RM_SPEED_ADAPT_EXEMPT = 1 << 10, ///< Remove speed adaptation exemption TRPRF_RM_SPEED_ADAPT_EXEMPT = 1 << 10, ///< Remove speed adaptation exemption
TRPRF_SIGNAL_MODE_NORMAL = 1 << 11, ///< Combined normal/shunt signal mode control: normal TRPRF_SIGNAL_MODE_NORMAL = 1 << 11, ///< Combined normal/shunt signal mode control: normal
TRPRF_SIGNAL_MODE_SHUNT = 1 << 12, ///< Combined normal/shunt signal mode control: shunt TRPRF_SIGNAL_MODE_SHUNT = 1 << 12, ///< Combined normal/shunt signal mode control: shunt
TRPRF_REVERSE_AT = 1 << 13, ///< Reverse at PBS signal is set
}; };
DECLARE_ENUM_AS_BIT_SET(TraceRestrictProgramResultFlags) DECLARE_ENUM_AS_BIT_SET(TraceRestrictProgramResultFlags)
@ -476,6 +479,7 @@ enum TraceRestrictProgramActionsUsedFlags : uint32_t {
TRPAUF_RESERVE_THROUGH_ALWAYS = 1 << 17, ///< Reserve through action is unconditionally set TRPAUF_RESERVE_THROUGH_ALWAYS = 1 << 17, ///< Reserve through action is unconditionally set
TRPAUF_CMB_SIGNAL_MODE_CTRL = 1 << 18, ///< Combined normal/shunt signal mode control TRPAUF_CMB_SIGNAL_MODE_CTRL = 1 << 18, ///< Combined normal/shunt signal mode control
TRPAUF_ORDER_CONDITIONALS = 1 << 19, ///< Order conditionals are present TRPAUF_ORDER_CONDITIONALS = 1 << 19, ///< Order conditionals are present
TRPAUF_REVERSE_AT = 1 << 20, ///< Reverse at signal
}; };
DECLARE_ENUM_AS_BIT_SET(TraceRestrictProgramActionsUsedFlags) DECLARE_ENUM_AS_BIT_SET(TraceRestrictProgramActionsUsedFlags)

@ -335,10 +335,14 @@ static const TraceRestrictDropDownListSet _train_status_value = {
static const StringID _reverse_value_str[] = { static const StringID _reverse_value_str[] = {
STR_TRACE_RESTRICT_REVERSE_SIG, STR_TRACE_RESTRICT_REVERSE_SIG,
STR_TRACE_RESTRICT_REVERSE_SIG_CANCEL, STR_TRACE_RESTRICT_REVERSE_SIG_CANCEL,
STR_TRACE_RESTRICT_REVERSE_AT_SIG,
STR_TRACE_RESTRICT_REVERSE_AT_SIG_CANCEL,
}; };
static const uint _reverse_value_val[] = { static const uint _reverse_value_val[] = {
TRRVF_REVERSE_BEHIND, TRRVF_REVERSE_BEHIND,
TRRVF_CANCEL_REVERSE_BEHIND, TRRVF_CANCEL_REVERSE_BEHIND,
TRRVF_REVERSE_AT,
TRRVF_CANCEL_REVERSE_AT,
}; };
/** value drop down list for reverse types strings and values */ /** value drop down list for reverse types strings and values */
@ -1614,6 +1618,14 @@ static void DrawInstructionString(const TraceRestrictProgram *prog, TraceRestric
instruction_string = STR_TRACE_RESTRICT_REVERSE_SIG_CANCEL; instruction_string = STR_TRACE_RESTRICT_REVERSE_SIG_CANCEL;
break; break;
case TRRVF_REVERSE_AT:
instruction_string = STR_TRACE_RESTRICT_REVERSE_AT_SIG;
break;
case TRRVF_CANCEL_REVERSE_AT:
instruction_string = STR_TRACE_RESTRICT_REVERSE_AT_SIG_CANCEL;
break;
default: default:
NOT_REACHED(); NOT_REACHED();
break; break;

@ -86,6 +86,7 @@ void FreeTrainTrackReservation(Train *v, TileIndex origin = INVALID_TILE, Trackd
enum TryPathReserveResultFlags { enum TryPathReserveResultFlags {
TPRRF_NONE = 0, ///< No flags TPRRF_NONE = 0, ///< No flags
TPRRF_RESERVATION_OK = 0x01, ///< Reservation OK TPRRF_RESERVATION_OK = 0x01, ///< Reservation OK
TPRRF_REVERSE_AT_SIGNAL = 0x02, ///< Reverse at signal
}; };
DECLARE_ENUM_AS_BIT_SET(TryPathReserveResultFlags) DECLARE_ENUM_AS_BIT_SET(TryPathReserveResultFlags)

@ -84,6 +84,7 @@ DECLARE_ENUM_AS_BIT_SET(ChooseTrainTrackFlags)
enum ChooseTrainTrackResultFlags { enum ChooseTrainTrackResultFlags {
CTTRF_NONE = 0, ///< No flags CTTRF_NONE = 0, ///< No flags
CTTRF_RESERVATION_MADE = 0x01, ///< A reservation was made CTTRF_RESERVATION_MADE = 0x01, ///< A reservation was made
CTTRF_REVERSE_AT_SIGNAL = 0x02, ///< Reverse at signal
}; };
DECLARE_ENUM_AS_BIT_SET(ChooseTrainTrackResultFlags) DECLARE_ENUM_AS_BIT_SET(ChooseTrainTrackResultFlags)
@ -4329,12 +4330,12 @@ static void TryLongReserveChooseTrainTrack(Train *v, TileIndex tile, Trackdir td
long_reserve = (out.flags & TRPRF_LONG_RESERVE); long_reserve = (out.flags & TRPRF_LONG_RESERVE);
} }
if (!long_reserve) return; if (!long_reserve) return;
if (prog != nullptr && prog->actions_used_flags & (TRPAUF_WAIT_AT_PBS | TRPAUF_SLOT_ACQUIRE)) { if (prog != nullptr && prog->actions_used_flags & (TRPAUF_WAIT_AT_PBS | TRPAUF_SLOT_ACQUIRE | TRPAUF_REVERSE_AT)) {
TraceRestrictProgramResult out; TraceRestrictProgramResult out;
TraceRestrictProgramInput input(exit_tile, exit_td, nullptr, nullptr); TraceRestrictProgramInput input(exit_tile, exit_td, nullptr, nullptr);
input.permitted_slot_operations = TRPISP_ACQUIRE; input.permitted_slot_operations = TRPISP_ACQUIRE;
prog->Execute(v, input, out); prog->Execute(v, input, out);
if (out.flags & TRPRF_WAIT_AT_PBS) { if (out.flags & (TRPRF_WAIT_AT_PBS | TRPRF_REVERSE_AT)) {
return; return;
} }
} }
@ -4436,7 +4437,7 @@ static ChooseTrainTrackResult ChooseTrainTrack(Train *v, TileIndex tile, DiagDir
if (track != INVALID_TRACK && HasPbsSignalOnTrackdir(tile, TrackEnterdirToTrackdir(track, enterdir)) && !IsNoEntrySignal(tile, track)) { if (track != INVALID_TRACK && HasPbsSignalOnTrackdir(tile, TrackEnterdirToTrackdir(track, enterdir)) && !IsNoEntrySignal(tile, track)) {
if (IsRestrictedSignal(tile) && v->force_proceed != TFP_SIGNAL) { if (IsRestrictedSignal(tile) && v->force_proceed != TFP_SIGNAL) {
const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(tile, track); const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(tile, track);
if (prog != nullptr && prog->actions_used_flags & (TRPAUF_WAIT_AT_PBS | TRPAUF_SLOT_ACQUIRE | TRPAUF_TRAIN_NOT_STUCK)) { if (prog != nullptr && prog->actions_used_flags & (TRPAUF_WAIT_AT_PBS | TRPAUF_SLOT_ACQUIRE | TRPAUF_TRAIN_NOT_STUCK | TRPAUF_REVERSE_AT)) {
TraceRestrictProgramResult out; TraceRestrictProgramResult out;
TraceRestrictProgramInput input(tile, TrackEnterdirToTrackdir(track, enterdir), nullptr, nullptr); TraceRestrictProgramInput input(tile, TrackEnterdirToTrackdir(track, enterdir), nullptr, nullptr);
input.permitted_slot_operations = TRPISP_ACQUIRE; input.permitted_slot_operations = TRPISP_ACQUIRE;
@ -4444,7 +4445,10 @@ static ChooseTrainTrackResult ChooseTrainTrack(Train *v, TileIndex tile, DiagDir
if (out.flags & TRPRF_TRAIN_NOT_STUCK && !(v->track & TRACK_BIT_WORMHOLE) && !(v->track == TRACK_BIT_DEPOT)) { if (out.flags & TRPRF_TRAIN_NOT_STUCK && !(v->track & TRACK_BIT_WORMHOLE) && !(v->track == TRACK_BIT_DEPOT)) {
v->wait_counter = 0; v->wait_counter = 0;
} }
if (out.flags & TRPRF_WAIT_AT_PBS) { if (out.flags & TRPRF_REVERSE_AT) {
result_flags |= CTTRF_REVERSE_AT_SIGNAL;
}
if (out.flags & (TRPRF_WAIT_AT_PBS | TRPRF_REVERSE_AT)) {
if (mark_stuck) MarkTrainAsStuck(v, true); if (mark_stuck) MarkTrainAsStuck(v, true);
return { track, result_flags }; return { track, result_flags };
} }
@ -4672,7 +4676,7 @@ static ChooseTrainTrackResult ChooseTrainTrack(Train *v, TileIndex tile, DiagDir
* @param first_tile_okay True if no path should be reserved if the current tile is a safe position. * @param first_tile_okay True if no path should be reserved if the current tile is a safe position.
* @return Result flags. * @return Result flags.
*/ */
TryPathReserveResultFlags TryPathReserveResultFlags(Train *v, bool mark_as_stuck, bool first_tile_okay) TryPathReserveResultFlags TryPathReserveWithResultFlags(Train *v, bool mark_as_stuck, bool first_tile_okay)
{ {
dbg_assert(v->IsFrontEngine()); dbg_assert(v->IsFrontEngine());
@ -4767,16 +4771,20 @@ TryPathReserveResultFlags TryPathReserveResultFlags(Train *v, bool mark_as_stuck
if (Rail90DegTurnDisallowedTilesFromDiagDir(origin.tile, new_tile, exitdir)) reachable &= ~TrackCrossesTracks(TrackdirToTrack(origin.trackdir)); if (Rail90DegTurnDisallowedTilesFromDiagDir(origin.tile, new_tile, exitdir)) reachable &= ~TrackCrossesTracks(TrackdirToTrack(origin.trackdir));
bool res_made = false; TryPathReserveResultFlags result_flags = TPRRF_NONE;
if (reachable != TRACK_BIT_NONE) { if (reachable != TRACK_BIT_NONE) {
ChooseTrainTrackResult result = ChooseTrainTrack(v, new_tile, exitdir, reachable, CTTF_FORCE_RES | (mark_as_stuck ? CTTF_MARK_STUCK : CTTF_NONE)); ChooseTrainTrackResult result = ChooseTrainTrack(v, new_tile, exitdir, reachable, CTTF_FORCE_RES | (mark_as_stuck ? CTTF_MARK_STUCK : CTTF_NONE));
if (result.ctt_flags & CTTRF_RESERVATION_MADE) res_made = true; if (result.ctt_flags & CTTRF_RESERVATION_MADE) {
result_flags |= TPRRF_RESERVATION_OK;
} else if (result.ctt_flags & CTTRF_REVERSE_AT_SIGNAL) {
result_flags |= TPRRF_REVERSE_AT_SIGNAL;
}
} }
if (!res_made) { if ((result_flags & TPRRF_RESERVATION_OK) == 0) {
/* Free the depot reservation as well. */ /* Free the depot reservation as well. */
if (v->track == TRACK_BIT_DEPOT && v->tile == origin.tile) SetDepotReservation(v->tile, false); if (v->track == TRACK_BIT_DEPOT && v->tile == origin.tile) SetDepotReservation(v->tile, false);
return false; return result_flags;
} }
if (HasBit(v->flags, VRF_TRAIN_STUCK)) { if (HasBit(v->flags, VRF_TRAIN_STUCK)) {
@ -4785,7 +4793,7 @@ TryPathReserveResultFlags TryPathReserveResultFlags(Train *v, bool mark_as_stuck
} }
ClrBit(v->flags, VRF_TRAIN_STUCK); ClrBit(v->flags, VRF_TRAIN_STUCK);
if (_settings_game.vehicle.train_braking_model == TBM_REALISTIC) FillTrainReservationLookAhead(v); if (_settings_game.vehicle.train_braking_model == TBM_REALISTIC) FillTrainReservationLookAhead(v);
return true; return result_flags;
} }
@ -5679,10 +5687,13 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
if (!CheckCompatibleRail(v, gp.new_tile, enterdir)) goto invalid_rail; if (!CheckCompatibleRail(v, gp.new_tile, enterdir)) goto invalid_rail;
TrackBits chosen_track; TrackBits chosen_track;
bool reverse_at_signal = false;
if (prev == nullptr) { if (prev == nullptr) {
/* Currently the locomotive is active. Determine which one of the /* Currently the locomotive is active. Determine which one of the
* available tracks to choose */ * available tracks to choose */
chosen_track = TrackToTrackBits(ChooseTrainTrack(v, gp.new_tile, enterdir, bits, CTTF_MARK_STUCK | CTTF_NON_LOOKAHEAD).track); ChooseTrainTrackResult result = ChooseTrainTrack(v, gp.new_tile, enterdir, bits, CTTF_MARK_STUCK | CTTF_NON_LOOKAHEAD);
chosen_track = TrackToTrackBits(result.track);
reverse_at_signal = (result.ctt_flags & CTTRF_REVERSE_AT_SIGNAL);
dbg_assert_msg_tile(chosen_track & (bits | GetReservedTrackbits(gp.new_tile)), gp.new_tile, "0x%X, 0x%X, 0x%X", chosen_track, bits, GetReservedTrackbits(gp.new_tile)); dbg_assert_msg_tile(chosen_track & (bits | GetReservedTrackbits(gp.new_tile)), gp.new_tile, "0x%X, 0x%X, 0x%X", chosen_track, bits, GetReservedTrackbits(gp.new_tile));
if (v->force_proceed != TFP_NONE && IsPlainRailTile(gp.new_tile) && HasSignals(gp.new_tile)) { if (v->force_proceed != TFP_NONE && IsPlainRailTile(gp.new_tile) && HasSignals(gp.new_tile)) {
@ -5706,6 +5717,11 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse)
/* In front of a red signal */ /* In front of a red signal */
Trackdir i = FindFirstTrackdir(trackdirbits); Trackdir i = FindFirstTrackdir(trackdirbits);
if (reverse_at_signal) {
ClrBit(v->flags, VRF_TRAIN_STUCK);
goto reverse_train_direction;
}
/* Don't handle stuck trains here. */ /* Don't handle stuck trains here. */
if (HasBit(v->flags, VRF_TRAIN_STUCK)) return false; if (HasBit(v->flags, VRF_TRAIN_STUCK)) return false;
@ -6706,9 +6722,10 @@ static bool TrainLocoHandler(Train *v, bool mode)
bool turn_around = v->wait_counter % (_settings_game.pf.wait_for_pbs_path * DAY_TICKS) == 0 && _settings_game.pf.reverse_at_signals; bool turn_around = v->wait_counter % (_settings_game.pf.wait_for_pbs_path * DAY_TICKS) == 0 && _settings_game.pf.reverse_at_signals;
if (!turn_around && v->wait_counter % _settings_game.pf.path_backoff_interval != 0 && v->force_proceed == TFP_NONE) return true; if (!turn_around && v->wait_counter % _settings_game.pf.path_backoff_interval != 0 && v->force_proceed == TFP_NONE) return true;
if (!TryPathReserve(v)) { TryPathReserveResultFlags path_result = TryPathReserveWithResultFlags(v);
if ((path_result & TPRRF_RESERVATION_OK) == 0) {
/* Still stuck. */ /* Still stuck. */
if (turn_around) ReverseTrainDirection(v); if (turn_around || (path_result & TPRRF_REVERSE_AT_SIGNAL)) ReverseTrainDirection(v);
if (HasBit(v->flags, VRF_TRAIN_STUCK) && v->wait_counter > 2 * _settings_game.pf.wait_for_pbs_path * DAY_TICKS) { if (HasBit(v->flags, VRF_TRAIN_STUCK) && v->wait_counter > 2 * _settings_game.pf.wait_for_pbs_path * DAY_TICKS) {
/* Show message to player. */ /* Show message to player. */

Loading…
Cancel
Save