diff --git a/src/lang/english.txt b/src/lang/english.txt index 0219e46345..d5c48cacfc 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2602,6 +2602,8 @@ STR_TRACE_RESTRICT_UNDEFINED_COMPANY :Undefined compa STR_TRACE_RESTRICT_VALUE_CAPTION :{WHITE}Value STR_TRACE_RESTRICT_CAPTION :{WHITE}Routefinding restriction STR_TRACE_RESTRICT_CAPTION_SHARED :{WHITE}Routefinding restriction - shared by {COMMA} signals +STR_TRACE_RESTRICT_UP_BTN_TOOLTIP :{BLACK}Move the selected instruction up{}Ctrl+Click to move conditional instruction instead of whole block +STR_TRACE_RESTRICT_DOWN_BTN_TOOLTIP :{BLACK}Move the selected instruction down{}Ctrl+Click to move conditional instruction instead of whole block STR_TRACE_RESTRICT_TYPE_TOOLTIP :{BLACK}Type STR_TRACE_RESTRICT_COND_COMPARATOR_TOOLTIP :{BLACK}Comparison operator STR_TRACE_RESTRICT_COND_VALUE_TOOLTIP :{BLACK}Value @@ -2616,7 +2618,7 @@ 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_REMOVE_TOOLTIP :{BLACK}Remove the selected instruction{}Ctrl+Click to remove the selected conditional instruction but retain its contents STR_TRACE_RESTRICT_RESET_TOOLTIP :{BLACK}Reset the current signal (without affecting shared programs) STR_TRACE_RESTRICT_COPY_TOOLTIP :{BLACK}Copy program from another signal STR_TRACE_RESTRICT_SHARE_TOOLTIP :{BLACK}Share program with another signal @@ -2626,11 +2628,13 @@ STR_TRACE_RESTRICT_INSTRUCTION_LIST_TOOLTIP :{BLACK}Click an STR_TRACE_RESTRICT_ERROR_CAN_T_INSERT_ITEM :{WHITE}Can't insert instruction STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM :{WHITE}Can't modify instruction STR_TRACE_RESTRICT_ERROR_CAN_T_REMOVE_ITEM :{WHITE}Can't remove instruction +STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM :{WHITE}Can't move instruction STR_TRACE_RESTRICT_ERROR_VALUE_TOO_LARGE :{WHITE}Value too large, maximum is {DECIMAL} STR_TRACE_RESTRICT_ERROR_NO_PROGRAM :No trace restrict program exists STR_TRACE_RESTRICT_ERROR_OFFSET_TOO_LARGE :Offset too large STR_TRACE_RESTRICT_ERROR_CAN_T_CHANGE_CONDITIONALITY :Can't change conditionality STR_TRACE_RESTRICT_ERROR_CAN_T_REMOVE_ENDIF :Can't remove an 'end if' +STR_TRACE_RESTRICT_ERROR_CAN_T_SHALLOW_REMOVE_IF_ELIF :Can't shallow remove an 'if' block with one or more 'else if', 'or if' or 'else' blocks STR_TRACE_RESTRICT_ERROR_VALIDATE_END_CONDSTACK :Validation failed: condstack non-empty at exit STR_TRACE_RESTRICT_ERROR_VALIDATE_NO_IF :Validation failed: else/endif without opening if STR_TRACE_RESTRICT_ERROR_VALIDATE_DUP_ELSE :Validation failed: duplicate else diff --git a/src/pathfinder/yapf/yapf_costrail.hpp b/src/pathfinder/yapf/yapf_costrail.hpp index 2c5b134110..78d55af736 100644 --- a/src/pathfinder/yapf/yapf_costrail.hpp +++ b/src/pathfinder/yapf/yapf_costrail.hpp @@ -192,9 +192,9 @@ private: { const Node *node = static_cast(node_ptr); for (;;) { - TileIndex last_signal_tile = node->m_segment->m_last_signal_tile; + TileIndex last_signal_tile = node->m_last_non_reserve_through_signal_tile; if (last_signal_tile != INVALID_TILE) { - Trackdir last_signal_trackdir = node->m_segment->m_last_signal_td; + Trackdir last_signal_trackdir = node->m_last_non_reserve_through_signal_td; if (HasPbsSignalOnTrackdir(last_signal_tile, last_signal_trackdir)) { return last_signal_tile; } else { @@ -348,14 +348,18 @@ public: } } + bool is_reserve_through = false; if (ShouldCheckTraceRestrict(n, tile)) { TraceRestrictProgramResult out; - bool is_reserve_through = false; if (ExecuteTraceRestrict(n, tile, trackdir, cost, out, &is_reserve_through)) { return -1; } if (is_reserve_through) n.m_num_signals_res_through_passed++; } + if (!is_reserve_through) { + n.m_last_non_reserve_through_signal_tile = tile; + n.m_last_non_reserve_through_signal_td = trackdir; + } n.m_num_signals_passed++; n.m_segment->m_last_signal_tile = tile; diff --git a/src/pathfinder/yapf/yapf_node_rail.hpp b/src/pathfinder/yapf/yapf_node_rail.hpp index 3584366961..94c8d6c2f4 100644 --- a/src/pathfinder/yapf/yapf_node_rail.hpp +++ b/src/pathfinder/yapf/yapf_node_rail.hpp @@ -129,6 +129,8 @@ struct CYapfRailNodeT CYapfRailSegment *m_segment; uint16 m_num_signals_passed; uint16 m_num_signals_res_through_passed; + TileIndex m_last_non_reserve_through_signal_tile; + Trackdir m_last_non_reserve_through_signal_td; union { uint32 m_inherited_flags; struct { @@ -147,6 +149,8 @@ struct CYapfRailNodeT if (parent == NULL) { m_num_signals_passed = 0; m_num_signals_res_through_passed = 0; + m_last_non_reserve_through_signal_tile = INVALID_TILE; + m_last_non_reserve_through_signal_td = INVALID_TRACKDIR; flags_u.m_inherited_flags = 0; m_last_red_signal_type = SIGTYPE_NORMAL; /* We use PBS as initial signal type because if we are in @@ -163,6 +167,8 @@ struct CYapfRailNodeT } else { m_num_signals_passed = parent->m_num_signals_passed; m_num_signals_res_through_passed = parent->m_num_signals_res_through_passed; + m_last_non_reserve_through_signal_tile = parent->m_last_non_reserve_through_signal_tile; + m_last_non_reserve_through_signal_td = parent->m_last_non_reserve_through_signal_td; flags_u.m_inherited_flags = parent->flags_u.m_inherited_flags; m_last_red_signal_type = parent->m_last_red_signal_type; m_last_signal_type = parent->m_last_signal_type; diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index f87677e7b4..17c0ff3694 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -21,6 +21,7 @@ #include "group.h" #include "pathfinder/yapf/yapf_cache.h" #include +#include /** @file * @@ -855,13 +856,152 @@ static uint32 GetDualInstructionInitialValue(TraceRestrictItem item) } } +template T InstructionIteratorNext(T iter) +{ + return IsTraceRestrictDoubleItem(*iter) ? iter + 2 : iter + 1; +} + +template void InstructionIteratorAdvance(T &iter) +{ + iter = InstructionIteratorNext(iter); +} + +CommandCost TraceRestrictProgramRemoveItemAt(std::vector &items, uint32 offset, bool shallow_mode) +{ + TraceRestrictItem old_item = *TraceRestrictProgram::InstructionAt(items, offset); + if (IsTraceRestrictConditional(old_item) && GetTraceRestrictCondFlags(old_item) != TRCF_OR) { + bool remove_whole_block = false; + if (GetTraceRestrictCondFlags(old_item) == 0) { + if (GetTraceRestrictType(old_item) == TRIT_COND_ENDIF) { + // this is an end if, can't remove these + return_cmd_error(STR_TRACE_RESTRICT_ERROR_CAN_T_REMOVE_ENDIF); + } else { + // this is an opening if + remove_whole_block = true; + } + } + + uint32 recursion_depth = 1; + std::vector::iterator remove_start = TraceRestrictProgram::InstructionAt(items, offset); + std::vector::iterator remove_end = InstructionIteratorNext(remove_start); + + // iterate until matching end block found + for (; remove_end != items.end(); InstructionIteratorAdvance(remove_end)) { + TraceRestrictItem current_item = *remove_end; + if (IsTraceRestrictConditional(current_item)) { + if (GetTraceRestrictCondFlags(current_item) == 0) { + if (GetTraceRestrictType(current_item) == TRIT_COND_ENDIF) { + // this is an end if + recursion_depth--; + if (recursion_depth == 0) { + if (remove_whole_block) { + if (shallow_mode) { + // must erase endif first, as it is later in the vector + items.erase(remove_end, InstructionIteratorNext(remove_end)); + } else { + // inclusively remove up to here + InstructionIteratorAdvance(remove_end); + } + break; + } else { + // exclusively remove up to here + break; + } + } + } else { + // this is an opening if + recursion_depth++; + } + } else { + // this is an else/or type block + if (recursion_depth == 1 && !remove_whole_block) { + // exclusively remove up to here + recursion_depth = 0; + break; + } + if (recursion_depth == 1 && remove_whole_block && shallow_mode) { + // shallow-removing whole if block, and it contains an else/or if, bail out + return_cmd_error(STR_TRACE_RESTRICT_ERROR_CAN_T_SHALLOW_REMOVE_IF_ELIF); + } + } + } + } + if (recursion_depth != 0) return CMD_ERROR; // ran off the end + if (shallow_mode) { + items.erase(remove_start, InstructionIteratorNext(remove_start)); + } else { + items.erase(remove_start, remove_end); + } + } else { + std::vector::iterator remove_start = TraceRestrictProgram::InstructionAt(items, offset); + std::vector::iterator remove_end = InstructionIteratorNext(remove_start); + + items.erase(remove_start, remove_end); + } + return CommandCost(); +} + +CommandCost TraceRestrictProgramMoveItemAt(std::vector &items, uint32 &offset, bool up, bool shallow_mode) +{ + std::vector::iterator move_start = TraceRestrictProgram::InstructionAt(items, offset); + std::vector::iterator move_end = InstructionIteratorNext(move_start); + + TraceRestrictItem old_item = *move_start; + if (!shallow_mode) { + if (IsTraceRestrictConditional(old_item)) { + if (GetTraceRestrictCondFlags(old_item) != 0) { + // can't move or/else blocks + return_cmd_error(STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM); + } + if (GetTraceRestrictType(old_item) == TRIT_COND_ENDIF) { + // this is an end if, can't move these + return_cmd_error(STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM); + } + + uint32 recursion_depth = 1; + // iterate until matching end block found + for (; move_end != items.end(); InstructionIteratorAdvance(move_end)) { + TraceRestrictItem current_item = *move_end; + if (IsTraceRestrictConditional(current_item)) { + if (GetTraceRestrictCondFlags(current_item) == 0) { + if (GetTraceRestrictType(current_item) == TRIT_COND_ENDIF) { + // this is an end if + recursion_depth--; + if (recursion_depth == 0) { + // inclusively remove up to here + InstructionIteratorAdvance(move_end); + break; + } + } else { + // this is an opening if + recursion_depth++; + } + } + } + } + if (recursion_depth != 0) return CMD_ERROR; // ran off the end + } + } + + if (up) { + if (move_start == items.begin()) return_cmd_error(STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM); + std::rotate(TraceRestrictProgram::InstructionAt(items, offset - 1), move_start, move_end); + offset--; + } else { + if (move_end == items.end()) return_cmd_error(STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM); + std::rotate(move_start, move_end, InstructionIteratorNext(move_end)); + offset++; + } + return CommandCost(); +} + /** * The main command for editing a signal tracerestrict program. * @param tile The tile which contains the signal. * @param flags Internal command handler stuff. * Below apply for instruction modification actions only * @param p1 Bitstuffed items - * @param p2 Item, for insert and modify operations + * @param p2 Item, for insert and modify operations. Flags for instruction move operations * @return the cost of this operation (which is free), or an error */ CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) @@ -939,71 +1079,16 @@ CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, u break; } - case TRDCT_REMOVE_ITEM: { - TraceRestrictItem old_item = *TraceRestrictProgram::InstructionAt(items, offset); - if (IsTraceRestrictConditional(old_item) && GetTraceRestrictCondFlags(old_item) != TRCF_OR) { - bool remove_whole_block = false; - if (GetTraceRestrictCondFlags(old_item) == 0) { - if (GetTraceRestrictType(old_item) == TRIT_COND_ENDIF) { - // this is an end if, can't remove these - return_cmd_error(STR_TRACE_RESTRICT_ERROR_CAN_T_REMOVE_ENDIF); - } else { - // this is an opening if - remove_whole_block = true; - } - } + case TRDCT_REMOVE_ITEM: + case TRDCT_SHALLOW_REMOVE_ITEM: { + CommandCost res = TraceRestrictProgramRemoveItemAt(items, offset, type == TRDCT_SHALLOW_REMOVE_ITEM); + if (res.Failed()) return res; + break; + } - uint32 recursion_depth = 1; - std::vector::iterator remove_start = TraceRestrictProgram::InstructionAt(items, offset); - std::vector::iterator remove_end = remove_start + 1; - - // iterate until matching end block found - for (; remove_end != items.end(); ++remove_end) { - TraceRestrictItem current_item = *remove_end; - if (IsTraceRestrictConditional(current_item)) { - if (GetTraceRestrictCondFlags(current_item) == 0) { - if (GetTraceRestrictType(current_item) == TRIT_COND_ENDIF) { - // this is an end if - recursion_depth--; - if (recursion_depth == 0) { - if (remove_whole_block) { - // inclusively remove up to here - ++remove_end; - break; - } else { - // exclusively remove up to here - break; - } - } - } else { - // this is an opening if - recursion_depth++; - } - } else { - // this is an else/or type block - if (recursion_depth == 1 && !remove_whole_block) { - // exclusively remove up to here - recursion_depth = 0; - 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 { - 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); - } + case TRDCT_MOVE_ITEM: { + CommandCost res = TraceRestrictProgramMoveItemAt(items, offset, p2 & 1, p2 & 2); + if (res.Failed()) return res; break; } diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 604269d1e5..395ac644d4 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -629,6 +629,8 @@ enum TraceRestrictDoCommandType { 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_SHALLOW_REMOVE_ITEM, ///< shallow remove instruction at offset field, does not delete contents of block + TRDCT_MOVE_ITEM, ///< move instruction or block at offset field TRDCT_PROG_COPY, ///< copy program operation. Do not re-order this with respect to other values TRDCT_PROG_SHARE, ///< share program operation @@ -652,6 +654,9 @@ inline void TraceRestrictProgMgmtDoCommandP(TileIndex tile, Track track, TraceRe CommandCost CmdProgramSignalTraceRestrict(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text); CommandCost CmdProgramSignalTraceRestrictProgMgmt(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text); +CommandCost TraceRestrictProgramRemoveItemAt(std::vector &items, uint32 offset, bool shallow_mode); +CommandCost TraceRestrictProgramMoveItemAt(std::vector &items, uint32 &offset, bool up, bool shallow_mode); + void ShowTraceRestrictProgramWindow(TileIndex tile, Track track); void TraceRestrictRemoveDestinationID(TraceRestrictOrderCondAuxField type, uint16 index); diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index 9cd1d9bef1..117f2a9879 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -53,6 +53,9 @@ enum TraceRestrictWindowWidgets { TR_WIDGET_SEL_TOP_RIGHT, TR_WIDGET_SEL_SHARE, + TR_WIDGET_UP_BTN, + TR_WIDGET_DOWN_BTN, + TR_WIDGET_TYPE_COND, TR_WIDGET_TYPE_NONCOND, TR_WIDGET_CONDFLAGS, @@ -965,6 +968,11 @@ public: break; } } + } else if (GetTraceRestrictTypeProperties(item).value_type == TRVT_TILE_INDEX) { + TileIndex tile = *(TraceRestrictProgram::InstructionAt(this->GetProgram()->items, sel - 1) + 1); + if (tile != INVALID_TILE) { + ScrollMainWindowToTile(tile); + } } return; } @@ -1022,7 +1030,28 @@ public: return; } - TraceRestrictDoCommandP(tile, track, TRDCT_REMOVE_ITEM, this->selected_instruction - 1, 0, STR_TRACE_RESTRICT_ERROR_CAN_T_REMOVE_ITEM); + TraceRestrictDoCommandP(tile, track, _ctrl_pressed ? TRDCT_SHALLOW_REMOVE_ITEM : TRDCT_REMOVE_ITEM, + this->selected_instruction - 1, 0, STR_TRACE_RESTRICT_ERROR_CAN_T_REMOVE_ITEM); + break; + } + + case TR_WIDGET_UP_BTN: + case TR_WIDGET_DOWN_BTN: { + TraceRestrictItem item = this->GetSelected(); + if (this->GetOwner() != _local_company || item == 0) { + return; + } + + uint32 p2 = 0; + if (widget == TR_WIDGET_UP_BTN) p2 |= 1; + if (_ctrl_pressed) p2 |= 2; + + uint32 offset = this->selected_instruction - 1; + + this->IsUpDownBtnUsable(widget == TR_WIDGET_UP_BTN, true); + + TraceRestrictDoCommandP(tile, track, TRDCT_MOVE_ITEM, + offset, p2, STR_TRACE_RESTRICT_ERROR_CAN_T_MOVE_ITEM); break; } @@ -1580,6 +1609,12 @@ public: } } + virtual EventState OnCTRLStateChange() OVERRIDE + { + this->UpdateButtonState(); + return ES_NOT_HANDLED; + } + private: /** * Helper function to make start and end instructions (these are not stored in the actual program) @@ -1705,6 +1740,26 @@ private: this->UpdateButtonState(); } + bool IsUpDownBtnUsable(bool up, bool update_selection = false) { + const TraceRestrictProgram *prog = this->GetProgram(); + if (!prog) return false; + + TraceRestrictItem item = this->GetSelected(); + if (GetTraceRestrictType(item) == TRIT_NULL) return false; + + std::vector items = prog->items; // copy + uint32 offset = this->selected_instruction - 1; + if (TraceRestrictProgramMoveItemAt(items, offset, up, _ctrl_pressed).Succeeded()) { + TraceRestrictProgramActionsUsedFlags actions_used_flags; + if (TraceRestrictProgram::Validate(items, actions_used_flags).Succeeded()) { + if (update_selection) this->selected_instruction = offset + 1; + return true; + } + } + + return false; + } + /** * Update button states, text values, etc. */ @@ -1750,6 +1805,9 @@ private: this->DisableWidget(TR_WIDGET_BLANK_M); this->DisableWidget(TR_WIDGET_BLANK_R); + this->DisableWidget(TR_WIDGET_UP_BTN); + this->DisableWidget(TR_WIDGET_DOWN_BTN); + left_2_sel->SetDisplayedPlane(DPL2_BLANK); left_sel->SetDisplayedPlane(DPL_BLANK); middle_sel->SetDisplayedPlane(DPM_BLANK); @@ -1962,6 +2020,8 @@ private: this->EnableWidget(TR_WIDGET_INSERT); this->EnableWidget(TR_WIDGET_REMOVE); } + if (this->IsUpDownBtnUsable(true)) this->EnableWidget(TR_WIDGET_UP_BTN); + if (this->IsUpDownBtnUsable(false)) this->EnableWidget(TR_WIDGET_DOWN_BTN); } this->SetDirty(); @@ -2091,6 +2151,8 @@ static const NWidgetPart _nested_program_widgets[] = { // Button Bar NWidget(NWID_HORIZONTAL), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, TR_WIDGET_UP_BTN), SetMinimalSize(12, 12), SetDataTip(SPR_ARROW_UP, STR_TRACE_RESTRICT_UP_BTN_TOOLTIP), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, TR_WIDGET_DOWN_BTN), SetMinimalSize(12, 12), SetDataTip(SPR_ARROW_DOWN, STR_TRACE_RESTRICT_DOWN_BTN_TOOLTIP), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), NWidget(NWID_SELECTION, INVALID_COLOUR, TR_WIDGET_SEL_TOP_LEFT_2), NWidget(WWT_DROPDOWN, COLOUR_GREY, TR_WIDGET_TYPE_NONCOND), SetMinimalSize(124, 12), SetFill(1, 0), @@ -2127,7 +2189,7 @@ static const NWidgetPart _nested_program_widgets[] = { SetDataTip(STR_EMPTY, STR_NULL), SetResize(1, 0), EndContainer(), EndContainer(), - NWidget(WWT_IMGBTN, COLOUR_GREY, TR_WIDGET_GOTO_SIGNAL), SetMinimalSize(12, 12), SetDataTip(SPR_ARROW_RIGHT, STR_TRACE_RESTRICT_GOTO_SIGNAL_TOOLTIP), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, TR_WIDGET_GOTO_SIGNAL), SetMinimalSize(12, 12), SetDataTip(SPR_ARROW_RIGHT, STR_TRACE_RESTRICT_GOTO_SIGNAL_TOOLTIP), EndContainer(), /* Second button row. */