diff --git a/src/lang/english.txt b/src/lang/english.txt index 01520aebec..41b3ce1f43 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2421,6 +2421,8 @@ STR_TRACE_RESTRICT_VARIABLE_NEXT_ORDER :next order STR_TRACE_RESTRICT_VARIABLE_LAST_VISITED_STATION :last visited station STR_TRACE_RESTRICT_VARIABLE_CARGO :cargo STR_TRACE_RESTRICT_VARIABLE_ENTRY_DIRECTION :entry direction +STR_TRACE_RESTRICT_VARIABLE_PBS_ENTRY_SIGNAL :PBS entry signal +STR_TRACE_RESTRICT_VARIABLE_PBS_ENTRY_SIGNAL_LONG :entered signal of PBS block STR_TRACE_RESTRICT_VARIABLE_UNDEFINED :undefined STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_INTEGER :{STRING} {STRING} {STRING} {COMMA} then STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_SPEED :{STRING} {STRING} {STRING} {VELOCITY} then @@ -2430,6 +2432,7 @@ STR_TRACE_RESTRICT_CONDITIONAL_ORDER_DEPOT :{STRING} {STRIN STR_TRACE_RESTRICT_CONDITIONAL_CARGO :{STRING} train {STRING} cargo: {STRING} then STR_TRACE_RESTRICT_CONDITIONAL_ENTRY_DIRECTION :{STRING} train {STRING} entering from {STRING} tile edge then STR_TRACE_RESTRICT_CONDITIONAL_ENTRY_SIGNAL_FACE :{STRING} train {STRING} entering from {STRING} of signal then +STR_TRACE_RESTRICT_CONDITIONAL_TILE_INDEX :{STRING} {STRING} {STRING} at {NUM} x {NUM} then STR_TRACE_RESTRICT_CONDITIONAL_UNDEFINED :{STRING} {STRING} {STRING} {RED}undefined {BLACK}{STRING}then STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_UNDEFINED :{STRING} {RED}undefined {BLACK}{STRING}then STR_TRACE_RESTRICT_PF_PENALTY_ITEM :Add pathfinder penalty: {COMMA} @@ -2461,6 +2464,7 @@ STR_TRACE_RESTRICT_COPY :{BLACK}Copy STR_TRACE_RESTRICT_SHARE :{BLACK}Share STR_TRACE_RESTRICT_UNSHARE :{BLACK}Unshare STR_TRACE_RESTRICT_SELECT_TARGET :{BLACK}Select Target +STR_TRACE_RESTRICT_SELECT_SIGNAL :{BLACK}Select Signal STR_TRACE_RESTRICT_INSERT_TOOLTIP :{BLACK}Insert an instruction STR_TRACE_RESTRICT_REMOVE_TOOLTIP :{BLACK}Remove the selected instruction STR_TRACE_RESTRICT_RESET_TOOLTIP :{BLACK}Reset the current signal (without affecting shared programs) diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp index 1215a9c6d2..25151b6b8f 100644 --- a/src/pathfinder/yapf/yapf_costrail.hpp +++ b/src/pathfinder/yapf/yapf_costrail.hpp @@ -189,12 +189,88 @@ private: IsRestrictedSignal(tile); } + /** + * This is called to retrieve the previous signal, as required + * This is not run all the time as it is somewhat expensive and most restrictions will not test for the previous signal + */ + static TileIndex TraceRestrictPreviousSignalCallback(const Train *v, const void *node_ptr) + { + const Node *node = static_cast(node_ptr); + for (;;) { + TileIndex last_signal_tile = node->m_segment->m_last_signal_tile; + if (last_signal_tile != INVALID_TILE) { + Trackdir last_signal_trackdir = node->m_segment->m_last_signal_td; + if (HasPbsSignalOnTrackdir(last_signal_tile, last_signal_trackdir)) { + return last_signal_tile; + } else { + return INVALID_TILE; + } + } + + if (node->m_parent) { + node = node->m_parent; + } else { + // scan forwards from vehicle position, for the case that train is waiting at/approaching PBS signal + + /* + * TODO: can this be made more efficient? + * This track scan will have been performed upstack, however extracting the entry signal + * during that scan and passing it through to this point would likely require relatively + * invasive changes to the pathfinder code, or at least an extra param on a number of wrapper + * functions between there and here, which would be best avoided. + */ + + TileIndex origin_tile = node->GetTile(); + Trackdir origin_trackdir = node->GetTrackdir(); + + TileIndex tile = v->tile; + Trackdir trackdir = v->GetVehicleTrackdir(); + + CFollowTrackRail ft(v); + + TileIndex candidate_tile = INVALID_TILE; + + for (;;) { + if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir)) { + if (HasPbsSignalOnTrackdir(tile, trackdir)) { + // found PBS signal + candidate_tile = tile; + } else { + // wrong type of signal + candidate_tile = INVALID_TILE; + } + } + + if (tile == origin_tile && trackdir == origin_trackdir) { + // reached pathfinder origin + return candidate_tile; + } + + // advance to next tile + if (!ft.Follow(tile, trackdir)) { + // ran out of track + return INVALID_TILE; + } + + if (KillFirstBit(ft.m_new_td_bits) != TRACKDIR_BIT_NONE) { + // reached a junction tile + return INVALID_TILE; + } + + tile = ft.m_new_tile; + trackdir = FindFirstTrackdir(ft.m_new_td_bits); + } + } + } + NOT_REACHED(); + } + // returns true if dead end bit has been set inline bool ExecuteTraceRestrict(Node& n, TileIndex tile, Trackdir trackdir, int& cost, TraceRestrictProgramResult &out) { const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(tile, TrackdirToTrack(trackdir)); if (prog) { - prog->Execute(Yapf().GetVehicle(), TraceRestrictProgramInput(tile, trackdir), out); + prog->Execute(Yapf().GetVehicle(), TraceRestrictProgramInput(tile, trackdir, &TraceRestrictPreviousSignalCallback, &n), out); if (out.flags & TRPRF_DENY) { n.m_segment->m_end_segment_reason |= ESRB_DEAD_END; return true; diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index 968ab49eb0..d5ee9aac59 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -217,6 +217,9 @@ void TraceRestrictProgram::Execute(const Train* v, const TraceRestrictProgramInp static std::vector condstack; condstack.clear(); + bool have_previous_signal = false; + TileIndex previous_signal_tile = INVALID_TILE; + size_t size = this->items.size(); for (size_t i = 0; i < size; i++) { TraceRestrictItem item = this->items[i]; @@ -313,6 +316,22 @@ void TraceRestrictProgram::Execute(const Train* v, const TraceRestrictProgramInp break; } + case TRIT_COND_PBS_ENTRY_SIGNAL: { + // TRVT_TILE_INDEX value type uses the next slot + i++; + uint32_t signal_tile = this->items[i]; + if (!have_previous_signal) { + if (input.previous_signal_callback) { + previous_signal_tile = input.previous_signal_callback(v, input.previous_signal_ptr); + } + have_previous_signal = true; + } + bool match = (signal_tile != INVALID_TILE) + && (previous_signal_tile == signal_tile); + result = TestBinaryConditionCommon(item, match); + break; + } + default: NOT_REACHED(); } @@ -395,6 +414,14 @@ CommandCost TraceRestrictProgram::Validate(const std::vector } HandleCondition(condstack, condflags, true); } + } else { + // check multi-word instructions + if (IsTraceRestrictDoubleItem(item)) { + i++; + if (i >= size) { + return_cmd_error(STR_TRACE_RESTRICT_ERROR_OFFSET_TOO_LARGE); // instruction ran off end + } + } } } if(!condstack.empty()) { @@ -403,6 +430,35 @@ CommandCost TraceRestrictProgram::Validate(const std::vector return CommandCost(); } +/** + * Convert an instruction index into an item array index + */ +size_t TraceRestrictProgram::InstructionOffsetToArrayOffset(const std::vector &items, size_t offset) +{ + size_t output_offset = 0; + size_t size = items.size(); + for (size_t i = 0; i < offset && output_offset < size; i++, output_offset++) { + if (IsTraceRestrictDoubleItem(items[output_offset])) { + output_offset++; + } + } + return output_offset; +} + +/** + * Convert an item array index into an instruction index + */ +size_t TraceRestrictProgram::ArrayOffsetToInstructionOffset(const std::vector &items, size_t offset) +{ + size_t output_offset = 0; + for (size_t i = 0; i < offset; i++, output_offset++) { + if (IsTraceRestrictDoubleItem(items[i])) { + i++; + } + } + return output_offset; +} + /** * Set the value and aux field of @p item, as per the value type in @p value_type */ @@ -413,6 +469,7 @@ void SetTraceRestrictValueDefault(TraceRestrictItem &item, TraceRestrictValueTyp case TRVT_INT: case TRVT_DENY: case TRVT_SPEED: + case TRVT_TILE_INDEX: SetTraceRestrictValue(item, 0); SetTraceRestrictAuxField(item, 0); break; @@ -595,6 +652,21 @@ static CommandCost TraceRestrictCheckTileIsUsable(TileIndex tile, Track track) return CommandCost(); } +/** + * Returns an appropriate default value for the second item of a dual-item instruction + * @p item is the first item of the instruction + */ +static uint32 GetDualInstructionInitialValue(TraceRestrictItem item) +{ + switch (GetTraceRestrictType(item)) { + case TRIT_COND_PBS_ENTRY_SIGNAL: + return INVALID_TILE; + + default: + NOT_REACHED(); + } +} + /** * The main command for editing a signal tracerestrict program. * @param tile The tile which contains the signal. @@ -641,28 +713,46 @@ CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, u switch (type) { case TRDCT_INSERT_ITEM: - items.insert(items.begin() + offset, item); + items.insert(TraceRestrictProgram::InstructionAt(items, offset), item); if (IsTraceRestrictConditional(item) && GetTraceRestrictCondFlags(item) == 0 && GetTraceRestrictType(item) != TRIT_COND_ENDIF) { // this is an opening if block, insert a corresponding end if TraceRestrictItem endif_item = 0; SetTraceRestrictType(endif_item, TRIT_COND_ENDIF); - items.insert(items.begin() + offset + 1, endif_item); + items.insert(TraceRestrictProgram::InstructionAt(items, offset) + 1, endif_item); + } else if (IsTraceRestrictDoubleItem(item)) { + items.insert(TraceRestrictProgram::InstructionAt(items, offset) + 1, GetDualInstructionInitialValue(item)); } break; case TRDCT_MODIFY_ITEM: { - TraceRestrictItem old_item = items[offset]; - if (IsTraceRestrictConditional(old_item) != IsTraceRestrictConditional(item)) { + std::vector::iterator old_item = TraceRestrictProgram::InstructionAt(items, offset); + if (IsTraceRestrictConditional(*old_item) != IsTraceRestrictConditional(item)) { return_cmd_error(STR_TRACE_RESTRICT_ERROR_CAN_T_CHANGE_CONDITIONALITY); } - items[offset] = item; + bool old_is_dual = IsTraceRestrictDoubleItem(*old_item); + bool new_is_dual = IsTraceRestrictDoubleItem(item); + *old_item = item; + if (old_is_dual && !new_is_dual) { + items.erase(old_item + 1); + } else if (!old_is_dual && new_is_dual) { + items.insert(old_item + 1, GetDualInstructionInitialValue(item)); + } + break; + } + + case TRDCT_MODIFY_DUAL_ITEM: { + std::vector::iterator old_item = TraceRestrictProgram::InstructionAt(items, offset); + if (!IsTraceRestrictDoubleItem(*old_item)) { + return CMD_ERROR; + } + *(old_item + 1) = p2; break; } case TRDCT_REMOVE_ITEM: { - TraceRestrictItem old_item = items[offset]; + TraceRestrictItem old_item = *TraceRestrictProgram::InstructionAt(items, offset); if (IsTraceRestrictConditional(old_item)) { bool remove_whole_block = false; if (GetTraceRestrictCondFlags(old_item) == 0) { @@ -676,7 +766,7 @@ CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, u } uint32 recursion_depth = 1; - std::vector::iterator remove_start = items.begin() + offset; + std::vector::iterator remove_start = TraceRestrictProgram::InstructionAt(items, offset); std::vector::iterator remove_end = remove_start + 1; // iterate until matching end block found @@ -709,12 +799,22 @@ CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, u break; } } + } else if (IsTraceRestrictDoubleItem(current_item)) { + // this is a double-item, jump over the next item as well + ++remove_end; } } if (recursion_depth != 0) return CMD_ERROR; // ran off the end items.erase(remove_start, remove_end); } else { - items.erase(items.begin() + offset); + std::vector::iterator remove_start = TraceRestrictProgram::InstructionAt(items, offset); + std::vector::iterator remove_end = remove_start + 1; + + if (IsTraceRestrictDoubleItem(old_item)) { + // this is a double-item, remove the next item as well + ++remove_end; + } + items.erase(remove_start, remove_end); } break; } diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 4e0f0f52bb..415eb8f7b3 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -106,6 +106,7 @@ enum TraceRestrictItemType { TRIT_COND_LAST_STATION = 14, ///< Test train last visited station TRIT_COND_CARGO = 15, ///< Test if train can carry cargo type TRIT_COND_ENTRY_DIRECTION = 16, ///< Test which side of signal/signal tile is being entered from + TRIT_COND_PBS_ENTRY_SIGNAL = 17, ///< Test tile and PBS-state of previous signal /* space up to 31 */ }; @@ -176,11 +177,15 @@ DECLARE_ENUM_AS_BIT_SET(TraceRestrictProgramResultFlags) * Execution input of a TraceRestrictProgram */ struct TraceRestrictProgramInput { - TileIndex tile; ///< Tile of restrict signal, for direction testing - Trackdir trackdir; ///< Track direction on tile of restrict signal, for direction testing + typedef TileIndex PreviousSignalProc(const Train *v, const void *ptr); - TraceRestrictProgramInput(TileIndex tile_, Trackdir trackdir_) - : tile(tile_), trackdir(trackdir_) { } + TileIndex tile; ///< Tile of restrict signal, for direction testing + Trackdir trackdir; ///< Track direction on tile of restrict signal, for direction testing + PreviousSignalProc *previous_signal_callback; ///< Callback to retrieve tile and direction of previous signal, may be NULL + const void *previous_signal_ptr; ///< Opaque pointer suitable to be passed to previous_signal_callback + + TraceRestrictProgramInput(TileIndex tile_, Trackdir trackdir_, PreviousSignalProc *previous_signal_callback_, const void *previous_signal_ptr_) + : tile(tile_), trackdir(trackdir_), previous_signal_callback(previous_signal_callback_), previous_signal_ptr(previous_signal_ptr_) { } }; /** @@ -216,6 +221,46 @@ struct TraceRestrictProgram : TraceRestrictProgramPool::PoolItem<&_tracerestrict static CommandCost Validate(const std::vector &items); + static size_t InstructionOffsetToArrayOffset(const std::vector &items, size_t offset); + + static size_t ArrayOffsetToInstructionOffset(const std::vector &items, size_t offset); + + /** Call InstructionOffsetToArrayOffset on current program instruction list */ + size_t InstructionOffsetToArrayOffset(size_t offset) const + { + return TraceRestrictProgram::InstructionOffsetToArrayOffset(this->items, offset); + } + + /** Call ArrayOffsetToInstructionOffset on current program instruction list */ + size_t ArrayOffsetToInstructionOffset(size_t offset) const + { + return TraceRestrictProgram::ArrayOffsetToInstructionOffset(this->items, offset); + } + + /** Get number of instructions in @p items */ + static size_t GetInstructionCount(const std::vector &items) + { + return ArrayOffsetToInstructionOffset(items, items.size()); + } + + /** Call GetInstructionCount on current program instruction list */ + size_t GetInstructionCount() const + { + return TraceRestrictProgram::GetInstructionCount(this->items); + } + + /** Get an iterator to the instruction at a given @p instruction_offset in @p items */ + static std::vector::iterator InstructionAt(std::vector &items, size_t instruction_offset) + { + return items.begin() + TraceRestrictProgram::InstructionOffsetToArrayOffset(items, instruction_offset); + } + + /** Get a const_iterator to the instruction at a given @p instruction_offset in @p items */ + static std::vector::const_iterator InstructionAt(const std::vector &items, size_t instruction_offset) + { + return items.begin() + TraceRestrictProgram::InstructionOffsetToArrayOffset(items, instruction_offset); + } + /** * Call validation function on current program instruction list */ @@ -294,6 +339,12 @@ static inline bool IsTraceRestrictConditional(TraceRestrictItem item) return IsTraceRestrictTypeConditional(GetTraceRestrictType(item)); } +/** Is TraceRestrictItem a double-item type? */ +static inline bool IsTraceRestrictDoubleItem(TraceRestrictItem item) +{ + return GetTraceRestrictType(item) == TRIT_COND_PBS_ENTRY_SIGNAL; +} + /** * Categorisation of what is allowed in the TraceRestrictItem condition op field * see TraceRestrictTypePropertySet @@ -317,6 +368,7 @@ enum TraceRestrictValueType { TRVT_ORDER = 5, ///< takes an order target ID, as per the auxiliary field as type: TraceRestrictOrderCondAuxField TRVT_CARGO_ID = 6, ///< takes a CargoID TRVT_DIRECTION = 7, ///< takes a TraceRestrictDirectionTypeSpecialValue + TRVT_TILE_INDEX = 8, ///< takes a TileIndex in the next item slot }; /** @@ -373,6 +425,11 @@ static inline TraceRestrictTypePropertySet GetTraceRestrictTypeProperties(TraceR out.cond_type = TRCOT_BINARY; break; + case TRIT_COND_PBS_ENTRY_SIGNAL: + out.value_type = TRVT_TILE_INDEX; + out.cond_type = TRCOT_BINARY; + break; + default: NOT_REACHED(); break; @@ -432,14 +489,15 @@ static inline const TraceRestrictProgram *GetExistingTraceRestrictProgram(TileIn * Enumeration for command action type field, indicates what command to do */ enum TraceRestrictDoCommandType { - TRDCT_INSERT_ITEM = 0, ///< insert new instruction before offset field as given value - TRDCT_MODIFY_ITEM = 1, ///< modify instruction at offset field to given value - TRDCT_REMOVE_ITEM = 2, ///< remove instruction at offset field + TRDCT_INSERT_ITEM, ///< insert new instruction before offset field as given value + TRDCT_MODIFY_ITEM, ///< modify instruction at offset field to given value + TRDCT_MODIFY_DUAL_ITEM, ///< modify second item of dual-part instruction at offset field to given value + TRDCT_REMOVE_ITEM, ///< remove instruction at offset field - TRDCT_PROG_COPY = 3, ///< copy program operation. Do not re-order this with respect to other values - TRDCT_PROG_SHARE = 4, ///< share program operation - TRDCT_PROG_UNSHARE = 5, ///< unshare program (copy as a new program) - TRDCT_PROG_RESET = 6, ///< reset program state of signal + TRDCT_PROG_COPY, ///< copy program operation. Do not re-order this with respect to other values + TRDCT_PROG_SHARE, ///< share program operation + TRDCT_PROG_UNSHARE, ///< unshare program (copy as a new program) + TRDCT_PROG_RESET, ///< reset program state of signal }; void TraceRestrictDoCommandP(TileIndex tile, Track track, TraceRestrictDoCommandType type, uint32 offset, uint32 value, StringID error_msg); diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index cfd9a5eafe..215e753d80 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -57,6 +57,7 @@ enum TraceRestrictWindowWidgets { TR_WIDGET_VALUE_INT, TR_WIDGET_VALUE_DROPDOWN, TR_WIDGET_VALUE_DEST, + TR_WIDGET_VALUE_SIGNAL, TR_WIDGET_BLANK_L2, TR_WIDGET_BLANK_L, @@ -91,6 +92,7 @@ enum PanelWidgets { DPR_VALUE_INT = 0, DPR_VALUE_DROPDOWN, DPR_VALUE_DEST, + DPR_VALUE_SIGNAL, DPR_BLANK, // Share @@ -225,6 +227,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictI STR_TRACE_RESTRICT_VARIABLE_LAST_VISITED_STATION, STR_TRACE_RESTRICT_VARIABLE_CARGO, STR_TRACE_RESTRICT_VARIABLE_ENTRY_DIRECTION, + STR_TRACE_RESTRICT_VARIABLE_PBS_ENTRY_SIGNAL, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED, INVALID_STRING_ID, }; @@ -236,6 +239,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictI TRIT_COND_LAST_STATION, TRIT_COND_CARGO, TRIT_COND_ENTRY_DIRECTION, + TRIT_COND_PBS_ENTRY_SIGNAL, TRIT_COND_UNDEFINED, }; static const TraceRestrictDropDownListSet set_cond = { @@ -436,16 +440,26 @@ static void DrawInstructionStringConditionalIntegerCommon(TraceRestrictItem item SetDParam(3, GetTraceRestrictValue(item)); } +/** Common function for drawing an integer conditional instruction with an invalid value */ +static void DrawInstructionStringConditionalInvalidValue(TraceRestrictItem item, const TraceRestrictTypePropertySet &properties, StringID &instruction_string, bool selected) +{ + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_UNDEFINED; + DrawInstructionStringConditionalCommon(item, properties); + SetDParam(3, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY); +} + /** * Draws an instruction in the programming GUI - * @param instruction The instruction to draw + * @param prog The program (may be NULL) + * @param item The instruction to draw + * @param index The instruction index * @param y Y position for drawing * @param selected True, if the order is selected * @param indent How many levels the instruction is indented * @param left Left border for text drawing * @param right Right border for text drawing */ -static void DrawInstructionString(TraceRestrictItem item, int y, bool selected, int indent, int left, int right) +static void DrawInstructionString(const TraceRestrictProgram *prog, TraceRestrictItem item, int index, int y, bool selected, int indent, int left, int right) { StringID instruction_string = INVALID_STRING_ID; @@ -482,9 +496,7 @@ static void DrawInstructionString(TraceRestrictItem item, int y, bool selected, DrawInstructionStringConditionalIntegerCommon(item, properties); } else { // this is an invalid station, use a seperate string - instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_UNDEFINED; - DrawInstructionStringConditionalCommon(item, properties); - SetDParam(3, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY); + DrawInstructionStringConditionalInvalidValue(item, properties, instruction_string, selected); } break; @@ -526,6 +538,23 @@ static void DrawInstructionString(TraceRestrictItem item, int y, bool selected, SetDParam(2, GetDropDownStringByValue(&_direction_value, GetTraceRestrictValue(item))); break; + case TRVT_TILE_INDEX: { + assert(prog != NULL); + assert(GetTraceRestrictType(item) == TRIT_COND_PBS_ENTRY_SIGNAL); + TileIndex tile = *(TraceRestrictProgram::InstructionAt(prog->items, index - 1) + 1); + if (tile == INVALID_TILE) { + DrawInstructionStringConditionalInvalidValue(item, properties, instruction_string, selected); + } else { + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_TILE_INDEX; + SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]); + SetDParam(1, STR_TRACE_RESTRICT_VARIABLE_PBS_ENTRY_SIGNAL_LONG); + SetDParam(2, GetDropDownStringByValue(GetCondOpDropDownListSet(properties), GetTraceRestrictCondOp(item))); + SetDParam(3, TileX(tile)); + SetDParam(4, TileY(tile)); + } + break; + } + default: NOT_REACHED(); break; @@ -757,6 +786,11 @@ public: break; } + case TR_WIDGET_VALUE_SIGNAL: { + SetObjectToPlaceAction(widget, ANIMCURSOR_BUILDSIGNALS); + break; + } + case TR_WIDGET_GOTO_SIGNAL: ScrollMainWindowToTile(this->tile); break; @@ -900,6 +934,10 @@ public: OnPlaceObjectDestination(pt, tile, widget, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); break; + case TR_WIDGET_VALUE_SIGNAL: + OnPlaceObjectSignalTileValue(pt, tile, widget, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); + break; + default: NOT_REACHED(); break; @@ -996,6 +1034,32 @@ public: TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); } + /** + * Common OnPlaceObject handler for instruction value modification actions which involve selecting a signal tile value + */ + void OnPlaceObjectSignalTileValue(Point pt, TileIndex tile, int widget, int error_message) + { + TraceRestrictItem item = GetSelected(); + if (GetTraceRestrictTypeProperties(item).value_type != TRVT_TILE_INDEX) return; + + if (!IsPlainRailTile(tile)) { + ShowErrorMessage(error_message, STR_ERROR_THERE_IS_NO_RAILROAD_TRACK, WL_INFO); + return; + } + + if (GetPresentSignals(tile) == 0) { + ShowErrorMessage(error_message, STR_ERROR_THERE_ARE_NO_SIGNALS, WL_INFO); + return; + } + + if (!IsTileOwner(tile, _local_company)) { + ShowErrorMessage(error_message, STR_ERROR_AREA_IS_OWNED_BY_ANOTHER, WL_INFO); + return; + } + + TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_DUAL_ITEM, this->selected_instruction - 1, tile, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); + } + virtual void OnPlaceObjectAbort() OVERRIDE { this->RaiseButtons(); @@ -1053,7 +1117,7 @@ public: } if (i >= scroll_position && this->vscroll->IsVisible(i)) { - DrawInstructionString(item, y, i == this->selected_instruction, this_indent, r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT); + DrawInstructionString(prog, item, i, y, i == this->selected_instruction, this_indent, r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT); y += line_height; } } @@ -1109,7 +1173,7 @@ private: int GetItemCount(const TraceRestrictProgram *prog) const { if (prog) { - return 2 + prog->items.size(); + return 2 + prog->GetInstructionCount(); } else { return 2; } @@ -1141,17 +1205,17 @@ private: } if (prog) { - const std::vector &items = prog->items; + size_t instruction_count = prog->GetInstructionCount(); - if (static_cast(index) == items.size() + 1) { + if (static_cast(index) == instruction_count + 1) { return MakeSpecialItem(TRNTSV_END); } - if (static_cast(index) > items.size() + 1) { + if (static_cast(index) > instruction_count + 1) { return 0; } - return items[index - 1]; + return prog->items[prog->InstructionOffsetToArrayOffset(index - 1)]; } else { // No program defined, this is equivalent to an empty program if (index == 1) { @@ -1230,6 +1294,7 @@ private: this->RaiseWidget(TR_WIDGET_VALUE_INT); this->RaiseWidget(TR_WIDGET_VALUE_DROPDOWN); this->RaiseWidget(TR_WIDGET_VALUE_DEST); + this->RaiseWidget(TR_WIDGET_VALUE_SIGNAL); NWidgetStacked *left_2_sel = this->GetWidget(TR_WIDGET_SEL_TOP_LEFT_2); NWidgetStacked *left_sel = this->GetWidget(TR_WIDGET_SEL_TOP_LEFT); @@ -1244,6 +1309,7 @@ private: this->DisableWidget(TR_WIDGET_VALUE_INT); this->DisableWidget(TR_WIDGET_VALUE_DROPDOWN); this->DisableWidget(TR_WIDGET_VALUE_DEST); + this->DisableWidget(TR_WIDGET_VALUE_SIGNAL); this->DisableWidget(TR_WIDGET_INSERT); this->DisableWidget(TR_WIDGET_REMOVE); @@ -1404,6 +1470,11 @@ private: GetDropDownStringByValue(&_direction_value, GetTraceRestrictValue(item)); break; + case TRVT_TILE_INDEX: + right_sel->SetDisplayedPlane(DPR_VALUE_SIGNAL); + this->EnableWidget(TR_WIDGET_VALUE_SIGNAL); + break; + default: break; } @@ -1460,12 +1531,13 @@ private: std::vector items = prog->items; // copy - if (offset >= (items.size() + (replace ? 0 : 1))) return false; // off the end of the program + if (offset >= (TraceRestrictProgram::GetInstructionCount(items) + (replace ? 0 : 1))) return false; // off the end of the program + uint array_offset = TraceRestrictProgram::InstructionOffsetToArrayOffset(items, offset); if (replace) { - items[offset] = item; + items[array_offset] = item; } else { - items.insert(items.begin() + offset, item); + items.insert(items.begin() + array_offset, item); } return TraceRestrictProgram::Validate(items).Succeeded(); @@ -1539,6 +1611,8 @@ static const NWidgetPart _nested_program_widgets[] = { SetDataTip(STR_NULL, STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP), SetResize(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_VALUE_DEST), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_TRACE_RESTRICT_SELECT_TARGET, STR_TRACE_RESTRICT_SELECT_TARGET), SetResize(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_VALUE_SIGNAL), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_TRACE_RESTRICT_SELECT_SIGNAL, STR_TRACE_RESTRICT_SELECT_SIGNAL), SetResize(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, TR_WIDGET_BLANK_R), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_EMPTY, STR_NULL), SetResize(1, 0), EndContainer(),