diff --git a/src/lang/english.txt b/src/lang/english.txt index 3f79a65e01..2194d3afba 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2961,6 +2961,8 @@ STR_PROGSIG_INSERT_TOOLTIP :{BLACK}Insert a STR_PROGSIG_REMOVE_TOOLTIP :{BLACK}Remove the selected instruction STR_PROGSIG_REMOVE_PROGRAM_TOOLTIP :{BLACK}Remove entire program STR_PROGSIG_COPY_PROGRAM_TOOLTIP :{BLACK}Copy program from existing signal +STR_PROGSIG_COND_SLOT_TOOLTIP :{BLACK}Slot to compare occupancy of +STR_PROGSIG_COND_COUNTER_TOOLTIP :{BLACK}Counter to compare value of STR_PROGSIG_REMOVE_PROGRAM :{BLACK}Remove program STR_PROGSIG_COPY_PROGRAM :{BLACK}Copy program @@ -2981,7 +2983,16 @@ STR_PROGSIG_SET_SIGNAL :Make signal {ST STR_PROGSIG_COND_ALWAYS :always STR_PROGSIG_COND_NEVER :never STR_PROGSIG_COND_COMPARE :{STRING} {STRING} {NUM} +STR_PROGSIG_COND_SLOT_COMPARE :occupancy of {TRSLOT} {STRING} {NUM} +STR_PROGSIG_COND_SLOT_COMPARE_INVALID :occupancy of {PUSH_COLOUR}{RED}{STRING}{POP_COLOUR} {STRING} {NUM} +STR_PROGSIG_COND_SLOT_REMAINING_COMPARE :occupancy remaining of {TRSLOT} {STRING} {NUM} +STR_PROGSIG_COND_SLOT_REMAINING_COMPARE_INVALID :occupancy remaining of {PUSH_COLOUR}{RED}{STRING}{POP_COLOUR} {STRING} {NUM} +STR_PROGSIG_COND_COUNTER_COMPARE :value of {TRCOUNTER} {STRING} {NUM} +STR_PROGSIG_COND_COUNTER_COMPARE_INVALID :value of {PUSH_COLOUR}{RED}{STRING}{POP_COLOUR} {STRING} {NUM} STR_PROGSIG_COND_SIGNAL_STATE :signal state +STR_PROGSIG_COND_SLOT :slot occupancy +STR_PROGSIG_COND_SLOT_REMAINING :slot occupancy remaining +STR_PROGSIG_COND_COUNTER :counter value STR_PROGSIG_CONDVAR_SIGNAL_STATE_SPECIFIED :signal at {NUM} x {NUM} is green STR_PROGSIG_CONDVAR_SIGNAL_STATE_UNSPECIFIED :{RED}unspecified signal{STRING} is green STR_PROGSIG_CONDVAR_NUM_RED :red signals diff --git a/src/programmable_signals.cpp b/src/programmable_signals.cpp index 3ec31fdf75..04b31b2f4a 100644 --- a/src/programmable_signals.cpp +++ b/src/programmable_signals.cpp @@ -80,8 +80,23 @@ SignalSimpleCondition::SignalSimpleCondition(SignalConditionCode code) } } +bool SignalConditionComparable::EvaluateComparable(uint32 var_val) +{ + switch (this->comparator) { + case SGC_EQUALS: return var_val == this->value; + case SGC_NOT_EQUALS: return var_val != this->value; + case SGC_LESS_THAN: return var_val < this->value; + case SGC_LESS_THAN_EQUALS: return var_val <= this->value; + case SGC_MORE_THAN: return var_val > this->value; + case SGC_MORE_THAN_EQUALS: return var_val >= this->value; + case SGC_IS_TRUE: return var_val != 0; + case SGC_IS_FALSE: return !var_val; + default: NOT_REACHED(); + } +} + SignalVariableCondition::SignalVariableCondition(SignalConditionCode code) - : SignalCondition(code) + : SignalConditionComparable(code) { switch (this->cond_code) { case PSC_NUM_GREEN: comparator = SGC_NOT_EQUALS; break; @@ -99,20 +114,158 @@ SignalVariableCondition::SignalVariableCondition(SignalConditionCode code) case PSC_NUM_RED: var_val = vm.num_exits - vm.num_green; break; default: NOT_REACHED(); } + return this->EvaluateComparable(var_val); +} - switch (this->comparator) { - case SGC_EQUALS: return var_val == this->value; - case SGC_NOT_EQUALS: return var_val != this->value; - case SGC_LESS_THAN: return var_val < this->value; - case SGC_LESS_THAN_EQUALS: return var_val <= this->value; - case SGC_MORE_THAN: return var_val > this->value; - case SGC_MORE_THAN_EQUALS: return var_val >= this->value; - case SGC_IS_TRUE: return var_val != 0; - case SGC_IS_FALSE: return !var_val; +void AddSignalSlotDependency(TraceRestrictSlotID on, SignalReference dep) +{ + TraceRestrictSlot *slot = TraceRestrictSlot::Get(on); + assert(slot->owner == GetTileOwner(dep.tile)); + slot->progsig_dependants.push_back(dep); +} + +void RemoveSignalSlotDependency(TraceRestrictSlotID on, SignalReference dep) +{ + TraceRestrictSlot *slot = TraceRestrictSlot::Get(on); + auto ob = std::find(slot->progsig_dependants.begin(), slot->progsig_dependants.end(), dep); + + if (ob != slot->progsig_dependants.end()) slot->progsig_dependants.erase(ob); +} + +void AddSignalCounterDependency(TraceRestrictCounterID on, SignalReference dep) +{ + TraceRestrictCounter *ctr = TraceRestrictCounter::Get(on); + assert(ctr->owner == GetTileOwner(dep.tile)); + ctr->progsig_dependants.push_back(dep); +} + +void RemoveSignalCounterDependency(TraceRestrictCounterID on, SignalReference dep) +{ + TraceRestrictCounter *ctr = TraceRestrictCounter::Get(on); + auto ob = std::find(ctr->progsig_dependants.begin(), ctr->progsig_dependants.end(), dep); + + if (ob != ctr->progsig_dependants.end()) ctr->progsig_dependants.erase(ob); +} + +SignalSlotCondition::SignalSlotCondition(SignalConditionCode code, SignalReference this_sig, TraceRestrictSlotID slot_id) + : SignalConditionComparable(code), this_sig(this_sig), slot_id(slot_id) +{ + this->comparator = SGC_EQUALS; + this->value = 0; + if (this->CheckSlotValid()) { + AddSignalSlotDependency(this->slot_id, this->this_sig); + } +} + +bool SignalSlotCondition::IsSlotValid() const +{ + return TraceRestrictSlot::IsValidID(this->slot_id); +} + +bool SignalSlotCondition::CheckSlotValid() +{ + bool valid = this->IsSlotValid(); + if (!valid) { + this->Invalidate(); + } + return valid; +} + +void SignalSlotCondition::Invalidate() +{ + this->slot_id = INVALID_TRACE_RESTRICT_SLOT_ID; +} + +void SignalSlotCondition::SetSlot(TraceRestrictSlotID slot_id) +{ + if (this->IsSlotValid()) { + RemoveSignalSlotDependency(this->slot_id, this->this_sig); + } + this->slot_id = slot_id; + if (this->CheckSlotValid()) { + AddSignalSlotDependency(this->slot_id, this->this_sig); + } +} + +/*virtual*/ SignalSlotCondition::~SignalSlotCondition() +{ + if (this->IsSlotValid()) { + RemoveSignalSlotDependency(this->slot_id, this->this_sig); + } +} + +/*virtual*/ bool SignalSlotCondition::Evaluate(SignalVM& vm) +{ + if (!this->CheckSlotValid()) { + DEBUG(misc, 1, "Signal (%x, %d) has an invalid condition", this->this_sig.tile, this->this_sig.track); + return false; + } + + const TraceRestrictSlot *slot = TraceRestrictSlot::Get(this->slot_id); + switch (this->cond_code) { + case PSC_SLOT_OCC: return this->EvaluateComparable(slot->occupants.size()); + case PSC_SLOT_OCC_REM: return this->EvaluateComparable(slot->max_occupancy > slot->occupants.size() ? slot->max_occupancy - slot->occupants.size() : 0); default: NOT_REACHED(); } } +SignalCounterCondition::SignalCounterCondition(SignalReference this_sig, TraceRestrictCounterID ctr_id) + : SignalConditionComparable(PSC_COUNTER), this_sig(this_sig), ctr_id(ctr_id) +{ + this->comparator = SGC_EQUALS; + this->value = 0; + if (this->CheckCounterValid()) { + AddSignalCounterDependency(this->ctr_id, this->this_sig); + } +} + +bool SignalCounterCondition::IsCounterValid() const +{ + return TraceRestrictCounter::IsValidID(this->ctr_id); +} + +bool SignalCounterCondition::CheckCounterValid() +{ + bool valid = this->IsCounterValid(); + if (!valid) { + this->Invalidate(); + } + return valid; +} + +void SignalCounterCondition::Invalidate() +{ + this->ctr_id = INVALID_TRACE_RESTRICT_COUNTER_ID; +} + +void SignalCounterCondition::SetCounter(TraceRestrictCounterID ctr_id) +{ + if (this->IsCounterValid()) { + RemoveSignalCounterDependency(this->ctr_id, this->this_sig); + } + this->ctr_id = ctr_id; + if (this->CheckCounterValid()) { + AddSignalCounterDependency(this->ctr_id, this->this_sig); + } +} + +/*virtual*/ SignalCounterCondition::~SignalCounterCondition() +{ + if (this->IsCounterValid()) { + RemoveSignalCounterDependency(this->ctr_id, this->this_sig); + } +} + +/*virtual*/ bool SignalCounterCondition::Evaluate(SignalVM& vm) +{ + if (!this->CheckCounterValid()) { + DEBUG(misc, 1, "Signal (%x, %d) has an invalid condition", this->this_sig.tile, this->this_sig.track); + return false; + } + + return this->EvaluateComparable(TraceRestrictCounter::Get(this->ctr_id)->value); +} + SignalStateCondition::SignalStateCondition(SignalReference this_sig, TileIndex sig_tile, Trackdir sig_track) : SignalCondition(PSC_SIGNAL_STATE), this_sig(this_sig), sig_tile(sig_tile) @@ -419,11 +572,52 @@ void RemoveProgramDependencies(SignalReference dependency_target, SignalReferenc SignalIf* ifi = static_cast(insn); if (ifi->condition->ConditionCode() == PSC_SIGNAL_STATE) { SignalStateCondition* c = static_cast(ifi->condition); - if(c->sig_tile == dependency_target.tile && TrackdirToTrack(c->sig_track) == dependency_target.track) + if (c->sig_tile == dependency_target.tile && TrackdirToTrack(c->sig_track) == dependency_target.track) { c->Invalidate(); } } } + } + + InvalidateWindowData(WC_SIGNAL_PROGRAM, (signal_to_update.tile << 3) | signal_to_update.track); + AddTrackToSignalBuffer(signal_to_update.tile, signal_to_update.track, GetTileOwner(signal_to_update.tile)); + UpdateSignalsInBuffer(); +} + +void RemoveProgramSlotDependencies(TraceRestrictSlotID slot_being_removed, SignalReference signal_to_update) +{ + SignalProgram *prog = GetSignalProgram(signal_to_update); + for (SignalInstruction *insn : prog->instructions) { + if (insn->Opcode() == PSO_IF) { + SignalIf* ifi = static_cast(insn); + if (ifi->condition->ConditionCode() == PSC_SLOT_OCC || ifi->condition->ConditionCode() == PSC_SLOT_OCC_REM) { + SignalSlotCondition* c = static_cast(ifi->condition); + if (c->slot_id == slot_being_removed) { + c->Invalidate(); + } + } + } + } + + InvalidateWindowData(WC_SIGNAL_PROGRAM, (signal_to_update.tile << 3) | signal_to_update.track); + AddTrackToSignalBuffer(signal_to_update.tile, signal_to_update.track, GetTileOwner(signal_to_update.tile)); + UpdateSignalsInBuffer(); +} + +void RemoveProgramCounterDependencies(TraceRestrictCounterID ctr_being_removed, SignalReference signal_to_update) +{ + SignalProgram *prog = GetSignalProgram(signal_to_update); + for (SignalInstruction *insn : prog->instructions) { + if (insn->Opcode() == PSO_IF) { + SignalIf* ifi = static_cast(insn); + if (ifi->condition->ConditionCode() == PSC_COUNTER) { + SignalCounterCondition* c = static_cast(ifi->condition); + if (c->ctr_id == ctr_being_removed) { + c->Invalidate(); + } + } + } + } InvalidateWindowData(WC_SIGNAL_PROGRAM, (signal_to_update.tile << 3) | signal_to_update.track); AddTrackToSignalBuffer(signal_to_update.tile, signal_to_update.track, GetTileOwner(signal_to_update.tile)); @@ -586,6 +780,15 @@ CommandCost CmdModifySignalInstruction(TileIndex tile, DoCommandFlag flags, uint cond = new SignalStateCondition(SignalReference(tile, track), INVALID_TILE, INVALID_TRACKDIR); break; + case PSC_SLOT_OCC: + case PSC_SLOT_OCC_REM: + cond = new SignalSlotCondition(code, SignalReference(tile, track), INVALID_TRACE_RESTRICT_SLOT_ID); + break; + + case PSC_COUNTER: + cond = new SignalCounterCondition(SignalReference(tile, track), INVALID_TRACE_RESTRICT_COUNTER_ID); + break; + default: NOT_REACHED(); } si->SetCondition(cond); @@ -623,6 +826,47 @@ CommandCost CmdModifySignalInstruction(TileIndex tile, DoCommandFlag flags, uint if (!exec) return CommandCost(); sc->SetSignal(ti, td); } break; + + case PSC_SLOT_OCC: + case PSC_SLOT_OCC_REM: { + SignalSlotCondition *sc = static_cast(si->condition); + SignalConditionField f = (SignalConditionField) GB(p2, 1, 2); + uint32 val = GB(p2, 3, 27); + if (f == SCF_COMPARATOR) { + if (val > SGC_LAST) return_cmd_error(STR_ERR_PROGSIG_INVALID_COMPARATOR); + if (!exec) return CommandCost(); + sc->comparator = (SignalComparator) val; + } else if (f == SCF_VALUE) { + if (!exec) return CommandCost(); + sc->value = val; + } else if (f == SCF_SLOT_COUNTER) { + if (val != INVALID_TRACE_RESTRICT_SLOT_ID && !TraceRestrictSlot::IsValidID(val)) return CMD_ERROR; + if (!exec) return CommandCost(); + sc->SetSlot((TraceRestrictSlotID) val); + } else { + return_cmd_error(STR_ERR_PROGSIG_INVALID_CONDITION_FIELD); + } + } break; + + case PSC_COUNTER: { + SignalCounterCondition *sc = static_cast(si->condition); + SignalConditionField f = (SignalConditionField) GB(p2, 1, 2); + uint32 val = GB(p2, 3, 27); + if (f == SCF_COMPARATOR) { + if (val > SGC_LAST) return_cmd_error(STR_ERR_PROGSIG_INVALID_COMPARATOR); + if (!exec) return CommandCost(); + sc->comparator = (SignalComparator) val; + } else if (f == SCF_VALUE) { + if (!exec) return CommandCost(); + sc->value = val; + } else if (f == SCF_SLOT_COUNTER) { + if (val != INVALID_TRACE_RESTRICT_COUNTER_ID && !TraceRestrictCounter::IsValidID(val)) return CMD_ERROR; + if (!exec) return CommandCost(); + sc->SetCounter((TraceRestrictCounterID) val); + } else { + return_cmd_error(STR_ERR_PROGSIG_INVALID_CONDITION_FIELD); + } + } break; } } } break; @@ -739,6 +983,25 @@ static void CloneInstructions(SignalProgram *prog, SignalInstruction *insert_bef break; } + case PSC_SLOT_OCC: + case PSC_SLOT_OCC_REM: { + SignalSlotCondition *src = ((SignalSlotCondition *) src_cond); + SignalSlotCondition *cond = new SignalSlotCondition(code, SignalReference(prog->tile, prog->track), src->slot_id); + cond->comparator = src->comparator; + cond->value = src->value; + if_ins->SetCondition(cond); + break; + } + + case PSC_COUNTER: { + SignalCounterCondition *src = ((SignalCounterCondition *) src_cond); + SignalCounterCondition *cond = new SignalCounterCondition(SignalReference(prog->tile, prog->track), src->ctr_id); + cond->comparator = src->comparator; + cond->value = src->value; + if_ins->SetCondition(cond); + break; + } + default: NOT_REACHED(); } diff --git a/src/programmable_signals.h b/src/programmable_signals.h index da22d9bf75..d7b16cd8ca 100644 --- a/src/programmable_signals.h +++ b/src/programmable_signals.h @@ -10,6 +10,7 @@ #ifndef PROGRAMMABLE_SIGNALS_H #define PROGRAMMABLE_SIGNALS_H #include "rail_map.h" +#include "tracerestrict.h" #include "core/smallvec_type.hpp" #include @@ -129,8 +130,11 @@ enum SignalConditionCode { PSC_NUM_GREEN = 2, ///< Number of green signals behind this signal PSC_NUM_RED = 3, ///< Number of red signals behind this signal PSC_SIGNAL_STATE = 4, ///< State of another signal + PSC_SLOT_OCC = 5, ///< Slot occupancy + PSC_SLOT_OCC_REM = 6, ///< Slot occupancy remaining + PSC_COUNTER = 7, ///< Counter value - PSC_MAX = PSC_SIGNAL_STATE + PSC_MAX = PSC_COUNTER }; class SignalCondition { @@ -178,6 +182,17 @@ enum SignalComparator { enum SignalConditionField { SCF_COMPARATOR = 0, ///< the comparator (value from SignalComparator enum) SCF_VALUE = 1, ///< the value (integer value) + SCF_SLOT_COUNTER = 2, ///< the slot or counter +}; + +class SignalConditionComparable: public SignalCondition { +protected: + bool EvaluateComparable(uint32 var_val); + +public: + SignalConditionComparable(SignalConditionCode code) : SignalCondition(code) {} + SignalComparator comparator; + uint32 value; }; /** A conditon based upon comparing a variable and a value. This condition can be @@ -186,15 +201,12 @@ enum SignalConditionField { * The variable is specified by the conditon code, the comparison by @p comparator, and * the value to compare against by @p value. The condition returns the result of that value. */ -class SignalVariableCondition: public SignalCondition { +class SignalVariableCondition: public SignalConditionComparable { public: /// Constructs a condition refering to the value @p code refers to. Sets the /// comparator and value to sane defaults. SignalVariableCondition(SignalConditionCode code); - SignalComparator comparator; - uint32 value; - /// Evaluates the condition virtual bool Evaluate(SignalVM &vm); }; @@ -217,6 +229,40 @@ class SignalStateCondition: public SignalCondition { Trackdir sig_track; }; +/** A condition which is based upon the value of a slot. */ +class SignalSlotCondition: public SignalConditionComparable { + public: + SignalSlotCondition(SignalConditionCode code, SignalReference this_sig, TraceRestrictSlotID slot_id); + + void SetSlot(TraceRestrictSlotID slot_id); + bool IsSlotValid() const; + bool CheckSlotValid(); + void Invalidate(); + + virtual bool Evaluate(SignalVM& vm); + virtual ~SignalSlotCondition(); + + SignalReference this_sig; + TraceRestrictSlotID slot_id; +}; + +/** A condition which is based upon the value of a counter. */ +class SignalCounterCondition: public SignalConditionComparable { + public: + SignalCounterCondition(SignalReference this_sig, TraceRestrictCounterID ctr_id); + + void SetCounter(TraceRestrictCounterID ctr_id); + bool IsCounterValid() const; + bool CheckCounterValid(); + void Invalidate(); + + virtual bool Evaluate(SignalVM& vm); + virtual ~SignalCounterCondition(); + + SignalReference this_sig; + TraceRestrictCounterID ctr_id; +}; + // -- Instructions /** The special start and end pseudo instructions. diff --git a/src/programmable_signals_gui.cpp b/src/programmable_signals_gui.cpp index b332cd36bf..8b34d65427 100644 --- a/src/programmable_signals_gui.cpp +++ b/src/programmable_signals_gui.cpp @@ -23,16 +23,21 @@ #include "rail_map.h" #include "tile_cmd.h" #include "error.h" +#include "scope.h" #include "table/sprites.h" #include "table/strings.h" +DropDownList GetSlotDropDownList(Owner owner, TraceRestrictSlotID slot_id, int &selected); +DropDownList GetCounterDropDownList(Owner owner, TraceRestrictCounterID ctr_id, int &selected); + enum ProgramWindowWidgets { PROGRAM_WIDGET_CAPTION, PROGRAM_WIDGET_INSTRUCTION_LIST, PROGRAM_WIDGET_SCROLLBAR, PROGRAM_WIDGET_SEL_TOP_LEFT, + PROGRAM_WIDGET_SEL_TOP_AUX, PROGRAM_WIDGET_SEL_TOP_MIDDLE, PROGRAM_WIDGET_SEL_TOP_RIGHT, @@ -42,6 +47,8 @@ enum ProgramWindowWidgets { PROGRAM_WIDGET_COND_VALUE, PROGRAM_WIDGET_COND_GOTO_SIGNAL, PROGRAM_WIDGET_COND_SET_SIGNAL, + PROGRAM_WIDGET_COND_SLOT, + PROGRAM_WIDGET_COND_COUNTER, PROGRAM_WIDGET_GOTO_SIGNAL, PROGRAM_WIDGET_INSERT, @@ -56,6 +63,10 @@ enum PanelWidgets { DPL_COND_VARIABLE = 0, DPL_SET_STATE, + // Aux, + DPA_SLOT = 0, + DPA_COUNTER = 1, + // Middle DPM_COND_COMPARATOR = 0, DPM_COND_GOTO_SIGNAL, @@ -85,6 +96,9 @@ static bool IsConditionComparator(SignalCondition *cond) switch (cond->ConditionCode()) { case PSC_NUM_GREEN: case PSC_NUM_RED: + case PSC_SLOT_OCC: + case PSC_SLOT_OCC_REM: + case PSC_COUNTER: return true; default: @@ -98,6 +112,9 @@ static const StringID _program_condvar[] = { /* PSC_NUM_GREEN */ STR_PROGSIG_CONDVAR_NUM_GREEN, /* PSC_NUM_RED */ STR_PROGSIG_CONDVAR_NUM_RED, /* PSC_SIGNAL_STATE*/ STR_PROGSIG_COND_SIGNAL_STATE, + /* PSC_SLOT_OCC*/ STR_PROGSIG_COND_SLOT, + /* PSC_SLOT_OCC_REM*/ STR_PROGSIG_COND_SLOT_REMAINING, + /* PSC_COUNTER*/ STR_PROGSIG_COND_COUNTER, INVALID_STRING_ID }; @@ -125,10 +142,30 @@ static const StringID _program_sigstate[] = { static char *GetConditionString(SignalCondition *cond, char *buf, char *buflast, bool selected) { StringID string = INVALID_STRING_ID; - bool comparator = IsConditionComparator(cond); - - if (comparator) { - SignalVariableCondition *cv = static_cast(cond); + if (cond->ConditionCode() == PSC_SLOT_OCC || cond->ConditionCode() == PSC_SLOT_OCC_REM) { + SignalSlotCondition *scc = static_cast(cond); + if (scc->IsSlotValid()) { + string = (cond->ConditionCode() == PSC_SLOT_OCC_REM) ? STR_PROGSIG_COND_SLOT_REMAINING_COMPARE : STR_PROGSIG_COND_SLOT_COMPARE; + SetDParam(0, scc->slot_id); + } else { + string = (cond->ConditionCode() == PSC_SLOT_OCC_REM) ? STR_PROGSIG_COND_SLOT_REMAINING_COMPARE_INVALID : STR_PROGSIG_COND_SLOT_COMPARE_INVALID; + SetDParam(0, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED_RED); + } + SetDParam(1, _program_comparator[scc->comparator]); + SetDParam(2, scc->value); + } else if (cond->ConditionCode() == PSC_COUNTER) { + SignalCounterCondition *scc = static_cast(cond); + if (scc->IsCounterValid()) { + string = STR_PROGSIG_COND_COUNTER_COMPARE; + SetDParam(0, scc->ctr_id); + } else { + string = STR_PROGSIG_COND_COUNTER_COMPARE_INVALID; + SetDParam(0, STR_TRACE_RESTRICT_VARIABLE_UNDEFINED_RED); + } + SetDParam(1, _program_comparator[scc->comparator]); + SetDParam(2, scc->value); + } else if (IsConditionComparator(cond)) { + SignalConditionComparable *cv = static_cast(cond); string = STR_PROGSIG_COND_COMPARE; SetDParam(0, _program_condvar[cond->ConditionCode()]); SetDParam(1, _program_comparator[cv->comparator]); @@ -221,6 +258,8 @@ public: this->CreateNestedTree(desc); this->vscroll = this->GetScrollbar(PROGRAM_WIDGET_SCROLLBAR); + this->GetWidget(PROGRAM_WIDGET_SEL_TOP_AUX)->SetDisplayedPlane(SZSP_NONE); + this->current_aux_plane = SZSP_NONE; this->FinishInitNested((ref.tile << 3) | ref.track); program = GetSignalProgram(ref); @@ -278,7 +317,7 @@ public: if (!si || si->Opcode() != PSO_IF) return; SignalIf *sif = static_cast (si); - ShowDropDownMenu(this, _program_condvar, sif->condition->ConditionCode(), PROGRAM_WIDGET_COND_VARIABLE, 0, 0, 0); + ShowDropDownMenu(this, _program_condvar, sif->condition->ConditionCode(), PROGRAM_WIDGET_COND_VARIABLE, 0, _settings_client.gui.show_adv_tracerestrict_features ? 0 : 0xE0, 0); this->UpdateButtonState(); } break; @@ -287,7 +326,7 @@ public: if (!si || si->Opcode() != PSO_IF) return; SignalIf *sif = static_cast (si); if (!IsConditionComparator(sif->condition)) return; - SignalVariableCondition *vc = static_cast(sif->condition); + SignalConditionComparable *vc = static_cast(sif->condition); ShowDropDownMenu(this, _program_comparator, vc->comparator, PROGRAM_WIDGET_COND_COMPARATOR, 0, _program_comparator_hide_mask, 0); } break; @@ -297,7 +336,7 @@ public: if (!si || si->Opcode() != PSO_IF) return; SignalIf *sif = static_cast (si); if (!IsConditionComparator(sif->condition)) return; - SignalVariableCondition *vc = static_cast(sif->condition); + SignalConditionComparable *vc = static_cast(sif->condition); SetDParam(0, vc->value); //ShowQueryString(STR_JUST_INT, STR_PROGSIG_CONDITION_VALUE_CAPT, 5, 100, this, CS_NUMERAL, QSF_NONE); @@ -320,6 +359,30 @@ public: // this->RaiseWidget(PROGRAM_WIDGET_COND_GOTO_SIGNAL); } break; + case PROGRAM_WIDGET_COND_SLOT: { + SignalInstruction *si = this->GetSelected(); + if (!si || si->Opcode() != PSO_IF) return; + SignalIf *sif = static_cast (si); + if (sif->condition->ConditionCode() != PSC_SLOT_OCC && sif->condition->ConditionCode() != PSC_SLOT_OCC_REM) return; + SignalSlotCondition *sc = static_cast(sif->condition); + + int selected; + DropDownList list = GetSlotDropDownList(this->GetOwner(), sc->slot_id, selected); + if (!list.empty()) ShowDropDownList(this, std::move(list), selected, PROGRAM_WIDGET_COND_SLOT, 0, true); + } break; + + case PROGRAM_WIDGET_COND_COUNTER: { + SignalInstruction *si = this->GetSelected(); + if (!si || si->Opcode() != PSO_IF) return; + SignalIf *sif = static_cast (si); + if (sif->condition->ConditionCode() != PSC_COUNTER) return; + SignalCounterCondition *sc = static_cast(sif->condition); + + int selected; + DropDownList list = GetCounterDropDownList(this->GetOwner(), sc->ctr_id, selected); + if (!list.empty()) ShowDropDownList(this, std::move(list), selected, PROGRAM_WIDGET_COND_COUNTER, 0, true); + } break; + case PROGRAM_WIDGET_COND_SET_SIGNAL: { this->ToggleWidgetLoweredState(PROGRAM_WIDGET_COND_SET_SIGNAL); this->SetWidgetDirty(PROGRAM_WIDGET_COND_SET_SIGNAL); @@ -523,6 +586,19 @@ public: DoCommandP(this->tile, p1, p2, CMD_MODIFY_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_MODIFY_INSTRUCTION)); break; } + + case PROGRAM_WIDGET_COND_SLOT: + case PROGRAM_WIDGET_COND_COUNTER: { + uint64 p1 = 0, p2 = 0; + SB(p1, 0, 3, this->track); + SB(p1, 3, 16, ins->Id()); + + SB(p2, 0, 1, 1); + SB(p2, 1, 2, SCF_SLOT_COUNTER); + SB(p2, 3, 27, index); + + DoCommandP(this->tile, p1, p2, CMD_MODIFY_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_MODIFY_INSTRUCTION)); + } } } @@ -580,9 +656,29 @@ public: if (!insn || insn->Opcode() != PSO_IF) return; SignalIf *si = static_cast(insn); if (!IsConditionComparator(si->condition)) return; - SignalVariableCondition *vc = static_cast(si->condition); + SignalConditionComparable *vc = static_cast(si->condition); SetDParam(0, vc->value); } break; + + case PROGRAM_WIDGET_COND_SLOT: { + SetDParam(0, 0); + SignalInstruction *insn = this->GetSelected(); + if (!insn || insn->Opcode() != PSO_IF) return; + SignalIf *si = static_cast(insn); + if (si->condition->ConditionCode() != PSC_SLOT_OCC && si->condition->ConditionCode() != PSC_SLOT_OCC_REM) return; + SignalSlotCondition *sc = static_cast(si->condition); + SetDParam(0, sc->slot_id); + } break; + + case PROGRAM_WIDGET_COND_COUNTER: { + SetDParam(0, 0); + SignalInstruction *insn = this->GetSelected(); + if (!insn || insn->Opcode() != PSO_IF) return; + SignalIf *si = static_cast(insn); + if (si->condition->ConditionCode() != PSC_COUNTER) return; + SignalCounterCondition *sc = static_cast(si->condition); + SetDParam(0, sc->ctr_id); + } break; } } @@ -697,9 +793,17 @@ private: this->RaiseWidget(PROGRAM_WIDGET_COND_GOTO_SIGNAL); NWidgetStacked *left_sel = this->GetWidget(PROGRAM_WIDGET_SEL_TOP_LEFT); + NWidgetStacked *aux_sel = this->GetWidget(PROGRAM_WIDGET_SEL_TOP_AUX); NWidgetStacked *middle_sel = this->GetWidget(PROGRAM_WIDGET_SEL_TOP_MIDDLE); NWidgetStacked *right_sel = this->GetWidget(PROGRAM_WIDGET_SEL_TOP_RIGHT); + auto aux_sel_guard = scope_guard([&]() { + if (this->current_aux_plane != aux_sel->shown_plane) { + this->current_aux_plane = aux_sel->shown_plane; + this->ReInit(); + } + }); + // Disable all the modifier buttons - we will re-enable them if applicable this->DisableWidget(PROGRAM_WIDGET_SET_STATE); this->DisableWidget(PROGRAM_WIDGET_COND_VARIABLE); @@ -722,6 +826,8 @@ private: SignalInstruction *insn = GetSelected(); if (!insn) return; + aux_sel->SetDisplayedPlane(SZSP_NONE); + switch (insn->Opcode()) { case PSO_IF: { SignalIf *i = static_cast(insn); @@ -734,7 +840,7 @@ private: _program_condvar[i->condition->ConditionCode()]; if (IsConditionComparator(i->condition)) { - SignalVariableCondition *vc = static_cast(i->condition); + SignalConditionComparable *vc = static_cast(i->condition); this->EnableWidget(PROGRAM_WIDGET_COND_COMPARATOR); this->EnableWidget(PROGRAM_WIDGET_COND_VALUE); @@ -747,6 +853,17 @@ private: middle_sel->SetDisplayedPlane(DPM_COND_GOTO_SIGNAL); right_sel->SetDisplayedPlane(DPR_COND_SET_SIGNAL); } + + if (i->condition->ConditionCode() == PSC_SLOT_OCC || i->condition->ConditionCode() == PSC_SLOT_OCC_REM) { + SignalSlotCondition *scc = static_cast(i->condition); + this->GetWidget(PROGRAM_WIDGET_COND_SLOT)->widget_data = scc->IsSlotValid() ? STR_TRACE_RESTRICT_SLOT_NAME : STR_TRACE_RESTRICT_VARIABLE_UNDEFINED; + aux_sel->SetDisplayedPlane(DPA_SLOT); + } + if (i->condition->ConditionCode() == PSC_COUNTER) { + SignalCounterCondition *scc = static_cast(i->condition); + this->GetWidget(PROGRAM_WIDGET_COND_COUNTER)->widget_data = scc->IsCounterValid() ? STR_TRACE_RESTRICT_COUNTER_NAME : STR_TRACE_RESTRICT_VARIABLE_UNDEFINED; + aux_sel->SetDisplayedPlane(DPA_COUNTER); + } } break; case PSO_SET_SIGNAL: { @@ -778,6 +895,7 @@ private: GuiInstructionList instructions; int selected_instruction; Scrollbar *vscroll; + int current_aux_plane; }; static const NWidgetPart _nested_program_widgets[] = { @@ -792,7 +910,7 @@ static const NWidgetPart _nested_program_widgets[] = { // Program display NWidget(NWID_HORIZONTAL), - NWidget(WWT_PANEL, COLOUR_GREY, PROGRAM_WIDGET_INSTRUCTION_LIST), SetMinimalSize(372, 62), SetDataTip(0x0, STR_PROGSIG_CAPTION), SetResize(1, 1), EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, PROGRAM_WIDGET_INSTRUCTION_LIST), SetMinimalSize(372, 62), SetDataTip(0x0, STR_NULL), SetResize(1, 1), EndContainer(), NWidget(NWID_VSCROLLBAR, COLOUR_GREY, PROGRAM_WIDGET_SCROLLBAR), EndContainer(), @@ -805,6 +923,12 @@ static const NWidgetPart _nested_program_widgets[] = { NWidget(WWT_DROPDOWN, COLOUR_GREY, PROGRAM_WIDGET_SET_STATE), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_NULL, STR_PROGSIG_SIGNAL_STATE_TOOLTIP), SetResize(1, 0), EndContainer(), + NWidget(NWID_SELECTION, INVALID_COLOUR, PROGRAM_WIDGET_SEL_TOP_AUX), + NWidget(WWT_DROPDOWN, COLOUR_GREY, PROGRAM_WIDGET_COND_SLOT), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_NULL, STR_PROGSIG_COND_SLOT_TOOLTIP), SetResize(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_GREY, PROGRAM_WIDGET_COND_COUNTER), SetMinimalSize(124, 12), SetFill(1, 0), + SetDataTip(STR_NULL, STR_PROGSIG_COND_COUNTER_TOOLTIP), SetResize(1, 0), + EndContainer(), NWidget(NWID_SELECTION, INVALID_COLOUR, PROGRAM_WIDGET_SEL_TOP_MIDDLE), NWidget(WWT_DROPDOWN, COLOUR_GREY, PROGRAM_WIDGET_COND_COMPARATOR), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_NULL, STR_PROGSIG_COND_COMPARATOR_TOOLTIP), SetResize(1, 0), diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 2c4c2bc8e0..3c17267c68 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -72,7 +72,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_TRACE_RESTRICT_REVERSE, XSCF_NULL, 1, 1, "tracerestrict_reverse", 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_PROG_SIGS, XSCF_NULL, 1, 1, "programmable_signals", nullptr, nullptr, "SPRG" }, + { XSLFI_PROG_SIGS, XSCF_NULL, 2, 2, "programmable_signals", nullptr, nullptr, "SPRG" }, { XSLFI_ADJACENT_CROSSINGS, XSCF_NULL, 1, 1, "adjacent_crossings", nullptr, nullptr, nullptr }, { XSLFI_SAFER_CROSSINGS, XSCF_NULL, 1, 1, "safer_crossings", nullptr, nullptr, nullptr }, { XSLFI_DEPARTURE_BOARDS, XSCF_IGNORABLE_UNKNOWN, 1, 1, "departure_boards", nullptr, nullptr, nullptr }, diff --git a/src/saveload/signal_sl.cpp b/src/saveload/signal_sl.cpp index 5cf9e68037..7f2890d26b 100644 --- a/src/saveload/signal_sl.cpp +++ b/src/saveload/signal_sl.cpp @@ -64,6 +64,21 @@ static void WriteCondition(Buffer &b, SignalCondition *c) WriteVLI(b, sc->sig_track); } break; + case PSC_SLOT_OCC: + case PSC_SLOT_OCC_REM: { + SignalSlotCondition *cc = static_cast(c); + WriteVLI(b, cc->slot_id); + WriteVLI(b, cc->comparator); + WriteVLI(b, cc->value); + } break; + + case PSC_COUNTER: { + SignalCounterCondition *cc = static_cast(c); + WriteVLI(b, cc->ctr_id); + WriteVLI(b, cc->comparator); + WriteVLI(b, cc->value); + } break; + default: break; } @@ -88,6 +103,25 @@ static SignalCondition *ReadCondition(SignalReference this_sig) return new SignalStateCondition(this_sig, ti, td); } + case PSC_SLOT_OCC: + case PSC_SLOT_OCC_REM: { + TraceRestrictSlotID slot_id = (TraceRestrictSlotID) ReadVLI(); + SignalSlotCondition *c = new SignalSlotCondition(code, this_sig, slot_id); + c->comparator = (SignalComparator) ReadVLI(); + if(c->comparator > SGC_LAST) NOT_REACHED(); + c->value = ReadVLI(); + return c; + } break; + + case PSC_COUNTER: { + TraceRestrictCounterID ctr_id = (TraceRestrictCounterID) ReadVLI(); + SignalCounterCondition *c = new SignalCounterCondition(this_sig, ctr_id); + c->comparator = (SignalComparator) ReadVLI(); + if(c->comparator > SGC_LAST) NOT_REACHED(); + c->value = ReadVLI(); + return c; + } + default: return new SignalSimpleCondition(code); } diff --git a/src/signal.cpp b/src/signal.cpp index 9f2feb45de..9fe8ba6a25 100644 --- a/src/signal.cpp +++ b/src/signal.cpp @@ -845,18 +845,23 @@ void FreeSignalDependencies() _signal_dependencies.clear(); } +void UpdateSignalDependency(SignalReference sr) +{ + Trackdir td = TrackToTrackdir(sr.track); + _globset.Add(sr.tile, TrackdirToExitdir(td)); + _globset.Add(sr.tile, TrackdirToExitdir(ReverseTrackdir(td))); +} + static void MarkDependencidesForUpdate(SignalReference on) { SignalDependencyMap::iterator f = _signal_dependencies.find(on); if (f == _signal_dependencies.end()) return; SignalDependencyList &dependencies = f->second; - for (const SignalReference &i : dependencies) { - assert(GetTileOwner(i.tile) == GetTileOwner(on.tile)); + for (const SignalReference &sr : dependencies) { + assert(GetTileOwner(sr.tile) == GetTileOwner(on.tile)); - Trackdir td = TrackToTrackdir(i.track); - _globset.Add(i.tile, TrackdirToExitdir(td)); - _globset.Add(i.tile, TrackdirToExitdir(ReverseTrackdir(td))); + UpdateSignalDependency(sr); } } diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index c569e21e16..2279833b63 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -1721,6 +1721,7 @@ bool TraceRestrictSlot::Occupy(VehicleID id, bool force) SetBit(Train::Get(id)->flags, VRF_HAVE_SLOT); SetWindowDirty(WC_VEHICLE_DETAILS, id); InvalidateWindowClassesData(WC_TRACE_RESTRICT_SLOTS); + this->UpdateSignals(); return true; } @@ -1744,6 +1745,7 @@ void TraceRestrictSlot::Vacate(VehicleID id) { if (container_unordered_remove(this->occupants, id)) { this->DeIndex(id); + this->UpdateSignals(); } } @@ -1756,6 +1758,13 @@ void TraceRestrictSlot::Clear() this->occupants.clear(); } +void TraceRestrictSlot::UpdateSignals() { + for (SignalReference sr : this->progsig_dependants) { + AddTrackToSignalBuffer(sr.tile, sr.track, GetTileOwner(sr.tile)); + UpdateSignalsInBuffer(); + } +} + void TraceRestrictSlot::DeIndex(VehicleID id) { auto range = slot_vehicle_index.equal_range(id); @@ -1833,6 +1842,7 @@ void TraceRestrictRemoveVehicleFromAllSlots(VehicleID vehicle_id) for (auto it = range.first; it != range.second; ++it) { auto slot = TraceRestrictSlot::Get(it->second); container_unordered_remove(slot->occupants, vehicle_id); + slot->UpdateSignals(); } const bool anything_to_erase = range.first != range.second; @@ -1908,6 +1918,13 @@ void TraceRestrictRemoveSlotID(TraceRestrictSlotID index) InvalidateWindowClassesData(WC_VEHICLE_ORDERS); InvalidateWindowClassesData(WC_VEHICLE_TIMETABLE); } + + for (SignalReference sr : TraceRestrictSlot::Get(index)->progsig_dependants) { + if (IsProgrammableSignal(GetSignalType(sr.tile, sr.track))) { + extern void RemoveProgramSlotDependencies(TraceRestrictSlotID slot_being_removed, SignalReference signal_to_update); + RemoveProgramSlotDependencies(index, sr); + } + } } static bool IsUniqueSlotName(const char *name) @@ -2013,6 +2030,7 @@ CommandCost CmdAlterTraceRestrictSlot(TileIndex tile, DoCommandFlag flags, uint3 if (flags & DC_EXEC) { slot->max_occupancy = p2; + slot->UpdateSignals(); } } @@ -2021,6 +2039,7 @@ CommandCost CmdAlterTraceRestrictSlot(TileIndex tile, DoCommandFlag flags, uint3 InvalidateWindowClassesData(WC_TRACE_RESTRICT); InvalidateWindowClassesData(WC_TRACE_RESTRICT_SLOTS); InvalidateWindowClassesData(WC_VEHICLE_ORDERS); + InvalidateWindowClassesData(WC_SIGNAL_PROGRAM); } return CommandCost(); @@ -2082,6 +2101,10 @@ void TraceRestrictCounter::UpdateValue(int32 new_value) if (new_value != this->value) { this->value = new_value; InvalidateWindowClassesData(WC_TRACE_RESTRICT_COUNTERS); + for (SignalReference sr : this->progsig_dependants) { + AddTrackToSignalBuffer(sr.tile, sr.track, GetTileOwner(sr.tile)); + UpdateSignalsInBuffer(); + } } } @@ -2125,6 +2148,13 @@ void TraceRestrictRemoveCounterID(TraceRestrictCounterID index) InvalidateWindowClassesData(WC_VEHICLE_ORDERS); InvalidateWindowClassesData(WC_VEHICLE_TIMETABLE); } + + for (SignalReference sr : TraceRestrictCounter::Get(index)->progsig_dependants) { + if (IsProgrammableSignal(GetSignalType(sr.tile, sr.track))) { + extern void RemoveProgramCounterDependencies(TraceRestrictCounterID ctr_being_removed, SignalReference signal_to_update); + RemoveProgramCounterDependencies(index, sr); + } + } } /** @@ -2230,6 +2260,7 @@ CommandCost CmdAlterTraceRestrictCounter(TileIndex tile, DoCommandFlag flags, ui InvalidateWindowClassesData(WC_TRACE_RESTRICT); InvalidateWindowClassesData(WC_TRACE_RESTRICT_COUNTERS); InvalidateWindowClassesData(WC_VEHICLE_ORDERS); + InvalidateWindowClassesData(WC_SIGNAL_PROGRAM); } return CommandCost(); diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 1bc3167c6f..53de3b3b49 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -20,6 +20,7 @@ #include "tile_type.h" #include "group_type.h" #include "vehicle_type.h" +#include "signal_type.h" #include "3rdparty/cpp-btree/btree_map.h" #include #include @@ -873,6 +874,8 @@ struct TraceRestrictSlot : TraceRestrictSlotPool::PoolItem<&_tracerestrictslot_p std::string name; Owner owner; + std::vector progsig_dependants; + static void RebuildVehicleIndex(); static bool ValidateVehicleIndex(); static void ValidateSlotOccupants(std::function log); @@ -900,6 +903,7 @@ struct TraceRestrictSlot : TraceRestrictSlotPool::PoolItem<&_tracerestrictslot_p bool OccupyDryRun(VehicleID ids); void Vacate(VehicleID id); void Clear(); + void UpdateSignals(); private: void DeIndex(VehicleID id); @@ -913,6 +917,8 @@ struct TraceRestrictCounter : TraceRestrictCounterPool::PoolItem<&_tracerestrict std::string name; Owner owner; + std::vector progsig_dependants; + TraceRestrictCounter(CompanyID owner = INVALID_COMPANY) { this->owner = owner;