diff --git a/src/group_cmd.cpp b/src/group_cmd.cpp index 30034693b0..a3f1975555 100644 --- a/src/group_cmd.cpp +++ b/src/group_cmd.cpp @@ -22,6 +22,7 @@ #include "core/pool_func.hpp" #include "order_backup.h" #include "tbtr_template_vehicle.h" +#include "tracerestrict.h" #include "table/strings.h" @@ -351,6 +352,9 @@ CommandCost CmdDeleteGroup(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 /* Delete all template replacements using the just deleted group */ deleteIllegalTemplateReplacements(g->index); + /* notify tracerestrict that group is about to be deleted */ + TraceRestrictRemoveGroupID(g->index); + /* Delete the Replace Vehicle Windows */ DeleteWindowById(WC_REPLACE_VEHICLE, g->vehicle_type); delete g; diff --git a/src/group_gui.cpp b/src/group_gui.cpp index 5f08d595e4..638f65d696 100644 --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -103,6 +103,29 @@ static const NWidgetPart _nested_group_widgets[] = { EndContainer(), }; +/** Sort the groups by their name */ +int CDECL GroupNameSorter(const Group * const *a, const Group * const *b) +{ + static const Group *last_group[2] = { NULL, NULL }; + static char last_name[2][64] = { "", "" }; + + if (*a != last_group[0]) { + last_group[0] = *a; + SetDParam(0, (*a)->index); + GetString(last_name[0], STR_GROUP_NAME, lastof(last_name[0])); + } + + if (*b != last_group[1]) { + last_group[1] = *b; + SetDParam(0, (*b)->index); + GetString(last_name[1], STR_GROUP_NAME, lastof(last_name[1])); + } + + int r = strnatcmp(last_name[0], last_name[1]); // Sort by name (natural sorting). + if (r == 0) return (*a)->index - (*b)->index; + return r; +} + class VehicleGroupWindow : public BaseVehicleListWindow { private: /* Columns in the group list */ @@ -151,29 +174,6 @@ private: return has_children; } - /** Sort the groups by their name */ - static int CDECL GroupNameSorter(const Group * const *a, const Group * const *b) - { - static const Group *last_group[2] = { NULL, NULL }; - static char last_name[2][64] = { "", "" }; - - if (*a != last_group[0]) { - last_group[0] = *a; - SetDParam(0, (*a)->index); - GetString(last_name[0], STR_GROUP_NAME, lastof(last_name[0])); - } - - if (*b != last_group[1]) { - last_group[1] = *b; - SetDParam(0, (*b)->index); - GetString(last_name[1], STR_GROUP_NAME, lastof(last_name[1])); - } - - int r = strnatcmp(last_name[0], last_name[1]); // Sort by name (natural sorting). - if (r == 0) return (*a)->index - (*b)->index; - return r; - } - void ToogleGroupCollapse(GroupID group) { GroupID *item = this->collapsed_groups.Find(group); diff --git a/src/lang/english.txt b/src/lang/english.txt index f78692e450..be83d119bb 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2529,8 +2529,10 @@ 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_TRAIN_GROUP :train group STR_TRACE_RESTRICT_VARIABLE_TRAIN_OWNER :train owner STR_TRACE_RESTRICT_VARIABLE_UNDEFINED :undefined +STR_TRACE_RESTRICT_VARIABLE_UNDEFINED_RED :{RED}undefined STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_INTEGER :{STRING} {STRING} {STRING} {COMMA} then STR_TRACE_RESTRICT_CONDITIONAL_COMPARE_SPEED :{STRING} {STRING} {STRING} {VELOCITY} then STR_TRACE_RESTRICT_CONDITIONAL_ORDER_STATION :{STRING} {STRING} {STRING} {STATION} then @@ -2540,6 +2542,8 @@ STR_TRACE_RESTRICT_CONDITIONAL_CARGO :{STRING} train 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_GROUP :{STRING} train {STRING} in group: {GROUP} then +STR_TRACE_RESTRICT_CONDITIONAL_GROUP_STR :{STRING} train {STRING} in group: {STRING} {BLACK}{STRING}then STR_TRACE_RESTRICT_CONDITIONAL_OWNER :{STRING} {STRING} {STRING} {COMPANY} {COMPANY_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 diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 41f8bf905e..e77fb1129a 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -47,7 +47,7 @@ std::vector _sl_xv_discardable_chunk_ids; ///< list of chunks static const uint32 _sl_xv_slxi_chunk_version = 0; ///< current version os SLXI chunk const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { - { XSLFI_TRACE_RESTRICT, XSCF_NULL, 4, 4, "tracerestrict", NULL, NULL, "TRRM,TRRP" }, + { XSLFI_TRACE_RESTRICT, XSCF_NULL, 5, 5, "tracerestrict", NULL, NULL, "TRRM,TRRP" }, { 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 }, diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index 8ebd25d58f..71789c24a5 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -18,6 +18,7 @@ #include "window_func.h" #include "order_base.h" #include "cargotype.h" +#include "group.h" #include "pathfinder/yapf/yapf_cache.h" #include @@ -343,6 +344,11 @@ void TraceRestrictProgram::Execute(const Train* v, const TraceRestrictProgramInp break; } + case TRIT_COND_TRAIN_GROUP: { + result = TestBinaryConditionCommon(item, GroupIsInGroup(v->group_id, GetTraceRestrictValue(item))); + break; + } + case TRIT_COND_TRAIN_OWNER: { result = TestBinaryConditionCommon(item, v->owner == condvalue); break; @@ -484,6 +490,7 @@ CommandCost TraceRestrictProgram::Validate(const std::vector case TRIT_COND_CARGO: case TRIT_COND_ENTRY_DIRECTION: case TRIT_COND_PBS_ENTRY_SIGNAL: + case TRIT_COND_TRAIN_GROUP: case TRIT_COND_TRAIN_OWNER: break; @@ -583,6 +590,11 @@ void SetTraceRestrictValueDefault(TraceRestrictItem &item, TraceRestrictValueTyp SetTraceRestrictAuxField(item, TRPPAF_PRESET); break; + case TRVT_GROUP_INDEX: + SetTraceRestrictValue(item, INVALID_GROUP); + SetTraceRestrictAuxField(item, 0); + break; + case TRVT_OWNER: SetTraceRestrictValue(item, INVALID_OWNER); SetTraceRestrictAuxField(item, 0); @@ -1122,6 +1134,28 @@ void TraceRestrictRemoveDestinationID(TraceRestrictOrderCondAuxField type, uint1 InvalidateWindowClassesData(WC_TRACE_RESTRICT); } +/** + * This is called when a group is about to be deleted + * Scan program pool and change any references to it to the invalid group ID, to avoid dangling references + */ +void TraceRestrictRemoveGroupID(GroupID index) +{ + TraceRestrictProgram *prog; + + FOR_ALL_TRACE_RESTRICT_PROGRAMS(prog) { + for (size_t i = 0; i < prog->items.size(); i++) { + TraceRestrictItem &item = prog->items[i]; // note this is a reference, + if (GetTraceRestrictType(item) == TRIT_COND_TRAIN_GROUP && GetTraceRestrictValue(item) == index) { + SetTraceRestrictValueDefault(item, TRVT_GROUP_INDEX); // this updates the instruction in-place + } + if (IsTraceRestrictDoubleItem(item)) i++; + } + } + + // update windows + InvalidateWindowClassesData(WC_TRACE_RESTRICT); +} + /** * This is called when a company is about to be deleted or taken over * Scan program pool and change any references to it to the new company ID, to avoid dangling references diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 4342630fb9..abe8f30d38 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -17,6 +17,7 @@ #include "command_func.h" #include "rail_map.h" #include "tile_type.h" +#include "group_type.h" #include #include @@ -111,6 +112,7 @@ enum TraceRestrictItemType { 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 + TRIT_COND_TRAIN_GROUP = 18, ///< Test train group membership TRIT_COND_TRAIN_OWNER = 24, ///< Test train owner /* space up to 31 */ }; @@ -411,7 +413,8 @@ enum TraceRestrictValueType { TRVT_PF_PENALTY = 9, ///< takes a pathfinder penalty value or preset index, as per the auxiliary field as type: TraceRestrictPathfinderPenaltyAuxField TRVT_RESERVE_THROUGH = 10,///< takes a value 0 = reserve through, 1 = cancel previous reserve through TRVT_LONG_RESERVE = 11,///< takes a value 0 = long reserve, 1 = cancel previous long reserve - TRVT_OWNER = 12,///< takes a CompanyID + TRVT_GROUP_INDEX = 12,///< takes a GroupID + TRVT_OWNER = 40,///< takes a CompanyID }; /** @@ -473,6 +476,11 @@ static inline TraceRestrictTypePropertySet GetTraceRestrictTypeProperties(TraceR out.cond_type = TRCOT_BINARY; break; + case TRIT_COND_TRAIN_GROUP: + out.value_type = TRVT_GROUP_INDEX; + out.cond_type = TRCOT_BINARY; + break; + case TRIT_COND_TRAIN_OWNER: out.value_type = TRVT_OWNER; out.cond_type = TRCOT_BINARY; @@ -572,6 +580,7 @@ CommandCost CmdProgramSignalTraceRestrictProgMgmt(TileIndex tile, DoCommandFlag void ShowTraceRestrictProgramWindow(TileIndex tile, Track track); void TraceRestrictRemoveDestinationID(TraceRestrictOrderCondAuxField type, uint16 index); +void TraceRestrictRemoveGroupID(GroupID index); void TraceRestrictUpdateCompanyID(CompanyID old_company, CompanyID new_company); #endif /* TRACERESTRICT_H */ diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index 92dc62d236..46bbeb615f 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -24,6 +24,7 @@ #include "company_func.h" #include "tilehighlight_func.h" #include "widgets/dropdown_func.h" +#include "widgets/dropdown_type.h" #include "gui.h" #include "gfx_func.h" #include "rail_map.h" @@ -34,6 +35,8 @@ #include "depot_base.h" #include "error.h" #include "cargotype.h" +#include "sortlist_type.h" +#include "group.h" #include "table/sprites.h" #include "toolbar_gui.h" @@ -270,6 +273,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictI STR_TRACE_RESTRICT_VARIABLE_CARGO, STR_TRACE_RESTRICT_VARIABLE_ENTRY_DIRECTION, STR_TRACE_RESTRICT_VARIABLE_PBS_ENTRY_SIGNAL, + STR_TRACE_RESTRICT_VARIABLE_TRAIN_GROUP, STR_TRACE_RESTRICT_VARIABLE_TRAIN_OWNER, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED, INVALID_STRING_ID, @@ -283,6 +287,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictI TRIT_COND_CARGO, TRIT_COND_ENTRY_DIRECTION, TRIT_COND_PBS_ENTRY_SIGNAL, + TRIT_COND_TRAIN_GROUP, TRIT_COND_TRAIN_OWNER, TRIT_COND_UNDEFINED, }; @@ -314,6 +319,43 @@ static const TraceRestrictDropDownListSet *GetSortedCargoTypeDropDownListSet() return &cargo_list; } +/** + * Get a DropDownList of the group list + */ +static DropDownList *GetGroupDropDownList(Owner owner, GroupID group_id, int &selected) +{ + typedef GUIList GUIGroupList; + extern int CDECL GroupNameSorter(const Group * const *a, const Group * const *b); + + GUIGroupList list; + + const Group *g; + FOR_ALL_GROUPS(g) { + if (g->owner == owner && g->vehicle_type == VEH_TRAIN) { + *list.Append() = g; + } + } + + list.ForceResort(); + list.Sort(&GroupNameSorter); + + DropDownList *dlist = new DropDownList(); + selected = -1; + + if (group_id == DEFAULT_GROUP) selected = DEFAULT_GROUP; + *dlist->Append() = new DropDownListStringItem(STR_GROUP_DEFAULT_TRAINS, DEFAULT_GROUP, false); + + for (size_t i = 0; i < list.Length(); ++i) { + const Group *g = list[i]; + if (group_id == g->index) selected = group_id; + DropDownListParamStringItem *item = new DropDownListParamStringItem(STR_GROUP_NAME, g->index, false); + item->SetParam(0, g->index); + *dlist->Append() = item; + } + + return dlist; +} + static const StringID _cargo_cond_ops_str[] = { STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_CARGO_EQUALS, STR_TRACE_RESTRICT_CONDITIONAL_COMPARATOR_CARGO_NOT_EQUALS, @@ -637,6 +679,25 @@ static void DrawInstructionString(const TraceRestrictProgram *prog, TraceRestric break; } + case TRVT_GROUP_INDEX: { + assert(GetTraceRestrictCondFlags(item) <= TRCF_OR); + SetDParam(0, _program_cond_type[GetTraceRestrictCondFlags(item)]); + SetDParam(1, GetDropDownStringByValue(GetCondOpDropDownListSet(properties), GetTraceRestrictCondOp(item))); + if (GetTraceRestrictValue(item) == INVALID_GROUP) { + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_GROUP_STR; + SetDParam(2, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED_RED); + SetDParam(3, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY); + } else if (GetTraceRestrictValue(item) == DEFAULT_GROUP) { + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_GROUP_STR; + SetDParam(2, STR_GROUP_DEFAULT_TRAINS); + SetDParam(3, selected ? STR_TRACE_RESTRICT_WHITE : STR_EMPTY); + } else { + instruction_string = STR_TRACE_RESTRICT_CONDITIONAL_GROUP; + SetDParam(2, GetTraceRestrictValue(item)); + } + break; + } + case TRVT_OWNER: { assert(GetTraceRestrictCondFlags(item) <= TRCF_OR); CompanyID cid = static_cast(GetTraceRestrictValue(item)); @@ -918,6 +979,13 @@ public: this->ShowDropDownListWithValue(&_long_reserve_value, GetTraceRestrictValue(item), false, TR_WIDGET_VALUE_DROPDOWN, 0, 0, 0); break; + case TRVT_GROUP_INDEX: { + int selected; + DropDownList *dlist = GetGroupDropDownList(this->GetOwner(), GetTraceRestrictValue(item), selected); + ShowDropDownList(this, dlist, selected, TR_WIDGET_VALUE_DROPDOWN); + break; + } + case TRVT_OWNER: this->ShowCompanyDropDownListWithValue(static_cast(GetTraceRestrictValue(item)), false, TR_WIDGET_VALUE_DROPDOWN); break; @@ -993,8 +1061,8 @@ public: return; } - if (widget == TR_WIDGET_VALUE_DROPDOWN && this->value_drop_down_is_company) { - // this is a special company drop-down + if (widget == TR_WIDGET_VALUE_DROPDOWN && (this->value_drop_down_is_company || GetTraceRestrictTypeProperties(item).value_type == TRVT_GROUP_INDEX)) { + // this is a special company drop-down or group-index drop-down SetTraceRestrictValue(item, index); TraceRestrictDoCommandP(this->tile, this->track, TRDCT_MODIFY_ITEM, this->selected_instruction - 1, item, STR_TRACE_RESTRICT_ERROR_CAN_T_MODIFY_ITEM); return; @@ -1327,8 +1395,9 @@ public: case TR_WIDGET_VALUE_DROPDOWN: { TraceRestrictItem item = this->GetSelected(); - if (GetTraceRestrictTypeProperties(item).value_type == TRVT_PF_PENALTY && - GetTraceRestrictAuxField(item) == TRPPAF_VALUE) { + if ((GetTraceRestrictTypeProperties(item).value_type == TRVT_PF_PENALTY && + GetTraceRestrictAuxField(item) == TRPPAF_VALUE) + || GetTraceRestrictTypeProperties(item).value_type == TRVT_GROUP_INDEX) { SetDParam(0, GetTraceRestrictValue(item)); } break; @@ -1681,6 +1750,24 @@ private: GetTraceRestrictValue(item) ? STR_TRACE_RESTRICT_LONG_RESERVE_CANCEL : STR_TRACE_RESTRICT_LONG_RESERVE; break; + case TRVT_GROUP_INDEX: + right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN); + this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN); + switch (GetTraceRestrictValue(item)) { + case INVALID_GROUP: + this->GetWidget(TR_WIDGET_VALUE_DROPDOWN)->widget_data = STR_TRACE_RESTRICT_VARIABLE_UNDEFINED; + break; + + case DEFAULT_GROUP: + this->GetWidget(TR_WIDGET_VALUE_DROPDOWN)->widget_data = STR_GROUP_DEFAULT_TRAINS; + break; + + default: + this->GetWidget(TR_WIDGET_VALUE_DROPDOWN)->widget_data = STR_GROUP_NAME; + break; + } + break; + case TRVT_OWNER: right_sel->SetDisplayedPlane(DPR_VALUE_DROPDOWN); this->EnableWidget(TR_WIDGET_VALUE_DROPDOWN); @@ -1707,7 +1794,7 @@ private: void ShowDropDownListWithValue(const TraceRestrictDropDownListSet *list_set, uint value, bool missing_ok, int button, uint32 disabled_mask, uint32 hidden_mask, uint width) { - drop_down_list_mapping[button] = list_set; + this->drop_down_list_mapping[button] = list_set; int selected = GetDropDownListIndexByValue(list_set, value, missing_ok); if (button == TR_WIDGET_VALUE_DROPDOWN) this->value_drop_down_is_company = false; ShowDropDownMenu(this, list_set->string_array, selected, button, disabled_mask, hidden_mask, width);