# programmable_signals.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "programmable_signals.h"
    00014 #include "debug.h"
    00015 #include "command_func.h"
    00016 #include "table/strings.h"
    00017 #include "window_func.h"
    00018 #include "company_func.h"
    00019 #include "cmd_helper.h"
    00020 #include <assert.h>
    00021
    00022 ProgramList _signal_programs;
    00023 SpeedLimits _speedlimits;
    00024
    00025 SignalProgram::SignalProgram(TileIndex tile, Track track, bool raw)
    00026 {
    00027   this->tile  = tile;
    00028   this->track = track;
    00029   if (!raw) {
    00030     this->first_instruction = new SignalSpecial(this, PSO_FIRST);
    00031     this->last_instruction  = new SignalSpecial(this, PSO_LAST);
    00032     SignalSpecial::link(this->first_instruction, this->last_instruction);
    00033   }
    00034 }
    00035
    00036 SignalProgram::~SignalProgram()
    00037 {
    00038   this->DebugPrintProgram();
    00039   this->first_instruction->Remove();
    00040   delete this->first_instruction;
    00041   delete this->last_instruction;
    00042 }
    00043
    00044 struct SignalVM {
    00045   /* Initial information. */
    00046   uint num_exits;
    00047   uint num_green;
    00048   SignalProgram *program;
    00049
    00050   /* Current state. */
    00051   SignalInstruction *instruction;
    00052
    00053   /* Output state. */
    00054   SignalState state;
    00055
    00056   void Execute()
    00057   {
    00058     DEBUG(misc, 6, "Begining execution of programmable signal on tile %x, track %d",
    00059           this->program->tile, this->program->track);
    00060     do {
    00061       DEBUG(misc, 10, "  Executing instruction %d, opcode %d", this->instruction->Id(), this->instruction->Opcode());
    00062       this->instruction->Evaluate(*this);
    00063     } while (this->instruction);
    00064
    00065     DEBUG(misc, 6, "Completed");
    00066   }
    00067 };
    00068
    00071 SignalCondition::~SignalCondition()
    00072 {}
    00073
    00074 SignalSimpleCondition::SignalSimpleCondition(SignalConditionCode code)
    00075     : SignalCondition(code)
    00076 {}
    00077
    00078 /* virtual */ bool SignalSimpleCondition::Evaluate(SignalVM &vm)
    00079 {
    00080   switch (this->cond_code) {
    00081     case PSC_ALWAYS:    return true;
    00082     case PSC_NEVER:     return false;
    00083     default: NOT_REACHED();
    00084   }
    00085 }
    00086
    00087 SignalVariableCondition::SignalVariableCondition(SignalConditionCode code)
    00088     : SignalCondition(code)
    00089 {
    00090   switch (this->cond_code) {
    00091     case PSC_NUM_GREEN: comparator = SGC_NOT_EQUALS; break;
    00092     case PSC_NUM_RED:   comparator = SGC_EQUALS; break;
    00093     default: NOT_REACHED();
    00094   }
    00095   value = 0;
    00096 }
    00097
    00098 /*virtual*/ bool SignalVariableCondition::Evaluate(SignalVM &vm)
    00099 {
    00100   uint32 var_val;
    00101   switch (this->cond_code) {
    00102     case PSC_NUM_GREEN:  var_val = vm.num_green; break;
    00103     case PSC_NUM_RED:    var_val = vm.num_exits - vm.num_green; break;
    00104     default: NOT_REACHED();
    00105   }
    00106
    00107   switch (this->comparator) {
    00108     case SGC_EQUALS:            return var_val == this->value;
    00109     case SGC_NOT_EQUALS:        return var_val != this->value;
    00110     case SGC_LESS_THAN:         return var_val <  this->value;
    00111     case SGC_LESS_THAN_EQUALS:  return var_val <= this->value;
    00112     case SGC_MORE_THAN:         return var_val >  this->value;
    00113     case SGC_MORE_THAN_EQUALS:  return var_val >= this->value;
    00114     case SGC_IS_TRUE:           return var_val != 0;
    00115     case SGC_IS_FALSE:          return !var_val;
    00116     default: NOT_REACHED();
    00117   }
    00118 }
    00119
    00120 SignalStateCondition::SignalStateCondition(SignalReference this_sig, TileIndex sig_tile, Trackdir sig_track)
    00121     : SignalCondition(PSC_SIGNAL_STATE), this_sig(this_sig), sig_tile(sig_tile)
    00122     , sig_track(sig_track)
    00123 {
    00124   if (this->IsSignalValid()) {
    00125     AddSignalDependency(SignalReference(this->sig_tile, TrackdirToTrack(sig_track)), this->this_sig);
    00126   }
    00127 }
    00128
    00129 bool SignalStateCondition::IsSignalValid()
    00130 {
    00131   if (IsValidTile(this->sig_tile)) {
    00132     if (HasSignalOnTrackdir(this->sig_tile, this->sig_track)) {
    00133       return true;
    00134     } else {
    00135       Invalidate();
    00136     }
    00137   }
    00138   return false;
    00139 }
    00140
    00141 void SignalStateCondition::Invalidate()
    00142 {
    00143   this->sig_tile = INVALID_TILE;
    00144 }
    00145
    00146
    00147 void SignalStateCondition::SetSignal(TileIndex tile, Trackdir track)
    00148 {
    00149   if (this->IsSignalValid()) {
    00150     RemoveSignalDependency(SignalReference(this->sig_tile, TrackdirToTrack(sig_track)), this->this_sig);
    00151   }
    00152   this->sig_tile = tile;
    00153   this->sig_track = track;
    00154   if (this->IsSignalValid()) {
    00155     AddSignalDependency(SignalReference(this->sig_tile, TrackdirToTrack(sig_track)), this->this_sig);
    00156   }
    00157 }
    00158
    00159 /*virtual*/ SignalStateCondition::~SignalStateCondition()
    00160 {
    00161   if (this->IsSignalValid()) {
    00162     RemoveSignalDependency(SignalReference(this->sig_tile, TrackdirToTrack(sig_track)), this->this_sig);
    00163   }
    00164 }
    00165
    00166 /*virtual*/ bool SignalStateCondition::Evaluate(SignalVM& vm)
    00167 {
    00168   if (!this->IsSignalValid()) {
    00169     DEBUG(misc, 1, "Signal (%x, %d) has an invalid condition", this->this_sig.tile, this->this_sig.track);
    00170     return false;
    00171   }
    00172
    00173   return GetSignalStateByTrackdir(this->sig_tile, this->sig_track) == SIGNAL_STATE_GREEN;
    00174 }
    00175
    00178 SignalInstruction::SignalInstruction(SignalProgram *prog, SignalOpcode op)
    00179     : opcode(op), previous(NULL), program(prog)
    00180 {
    00181   *program->instructions.Append() = this;
    00182 }
    00183
    00184 SignalInstruction::~SignalInstruction()
    00185 {
    00186   SignalInstruction** pthis = program->instructions.Find(this);
    00187   assert(pthis != program->instructions.End());
    00188   program->instructions.Erase(pthis);
    00189 }
    00190
    00191 void SignalInstruction::Insert(SignalInstruction *before_insn)
    00192 {
    00193   this->previous = before_insn->Previous();
    00194   before_insn->Previous()->SetNext(this);
    00195   before_insn->SetPrevious(this);
    00196   this->SetNext(before_insn);
    00197 }
    00198
    00199 SignalSpecial::SignalSpecial(SignalProgram *prog, SignalOpcode op)
    00200     : SignalInstruction(prog, op)
    00201 {
    00202   assert(op == PSO_FIRST || op == PSO_LAST);
    00203   this->next = NULL;
    00204 }
    00205
    00206 /*virtual*/ void SignalSpecial::Remove()
    00207 {
    00208   if (opcode == PSO_FIRST) {
    00209     while (this->next->Opcode() != PSO_LAST) this->next->Remove();
    00210   } else if (opcode == PSO_LAST) {
    00211   } else NOT_REACHED();
    00212 }
    00213
    00214 /*static*/ void SignalSpecial::link(SignalSpecial *first, SignalSpecial *last)
    00215 {
    00216   assert(first->opcode == PSO_FIRST && last->opcode == PSO_LAST);
    00217   first->next    = last;
    00218   last->previous = first;
    00219 }
    00220
    00221 void SignalSpecial::Evaluate(SignalVM &vm)
    00222 {
    00223   if (this->opcode == PSO_FIRST) {
    00224     DEBUG(misc, 7, "  Executing First");
    00225     vm.instruction = this->next;
    00226   } else {
    00227     DEBUG(misc, 7, "  Executing Last");
    00228     vm.instruction = NULL;
    00229   }
    00230 }
    00231
    00232 /*virtual*/ void SignalSpecial::SetNext(SignalInstruction *next_insn)
    00233 {
    00234   this->next = next_insn;
    00235 }
    00236
    00237 SignalIf::PseudoInstruction::PseudoInstruction(SignalProgram *prog, SignalOpcode op)
    00238     : SignalInstruction(prog, op)
    00239   {}
    00240
    00241 SignalIf::PseudoInstruction::PseudoInstruction(SignalProgram *prog, SignalIf *block, SignalOpcode op)
    00242     : SignalInstruction(prog, op)
    00243 {
    00244   this->block = block;
    00245   if (op == PSO_IF_ELSE) {
    00246     previous = block;
    00247   } else if (op == PSO_IF_ENDIF) {
    00248     previous = block->if_true;
    00249   } else NOT_REACHED();
    00250 }
    00251
    00252 /*virtual*/ void SignalIf::PseudoInstruction::Remove()
    00253 {
    00254   if (opcode == PSO_IF_ELSE) {
    00255     this->block->if_true = NULL;
    00256     while(this->block->if_false) this->block->if_false->Remove();
    00257   } else if (opcode == PSO_IF_ENDIF) {
    00258     this->block->if_false = NULL;
    00259   } else NOT_REACHED();
    00260   delete this;
    00261 }
    00262
    00263 /*virtual*/ void SignalIf::PseudoInstruction::Evaluate(SignalVM &vm)
    00264 {
    00265   DEBUG(misc, 7, "  Executing If Pseudo Instruction %s", opcode == PSO_IF_ELSE ? "Else" : "Endif");
    00266   vm.instruction = this->block->after;
    00267 }
    00268
    00269 /*virtual*/ void SignalIf::PseudoInstruction::SetNext(SignalInstruction *next_insn)
    00270 {
    00271   if (this->opcode == PSO_IF_ELSE) {
    00272     this->block->if_false = next_insn;
    00273   } else if (this->opcode == PSO_IF_ENDIF) {
    00274     this->block->after = next_insn;
    00275   } else NOT_REACHED();
    00276 }
    00277
    00278 SignalIf::SignalIf(SignalProgram *prog, bool raw)
    00279     : SignalInstruction(prog, PSO_IF)
    00280 {
    00281   if (!raw) {
    00282     this->condition = new SignalSimpleCondition(PSC_ALWAYS);
    00283     this->if_true   = new PseudoInstruction(prog, this, PSO_IF_ELSE);
    00284     this->if_false  = new PseudoInstruction(prog, this, PSO_IF_ENDIF);
    00285     this->after     = NULL;
    00286   }
    00287 }
    00288
    00289 /*virtual*/ void SignalIf::Remove()
    00290 {
    00291   delete this->condition;
    00292   while (this->if_true)  this->if_true->Remove();
    00293
    00294   this->previous->SetNext(this->after);
    00295   this->after->SetPrevious(this->previous);
    00296   delete this;
    00297 }
    00298
    00299 /*virtual*/ void SignalIf::Insert(SignalInstruction *before_insn)
    00300 {
    00301   this->previous = before_insn->Previous();
    00302   before_insn->Previous()->SetNext(this);
    00303   before_insn->SetPrevious(this->if_false);
    00304   this->after    = before_insn;
    00305 }
    00306
    00307 void SignalIf::SetCondition(SignalCondition *cond)
    00308 {
    00309   assert(cond != this->condition);
    00310   delete this->condition;
    00311   this->condition = cond;
    00312 }
    00313
    00314 /*virtual*/ void SignalIf::Evaluate(SignalVM &vm)
    00315 {
    00316   bool is_true = this->condition->Evaluate(vm);
    00317   DEBUG(misc, 7, "  Executing If, taking %s branch", is_true ? "then" : "else");
    00318   if (is_true) {
    00319     vm.instruction = this->if_true;
    00320   } else {
    00321     vm.instruction = this->if_false;
    00322   }
    00323 }
    00324
    00325 /*virtual*/ void SignalIf::SetNext(SignalInstruction *next_insn)
    00326 {
    00327   this->if_true = next_insn;
    00328 }
    00329
    00330
    00331
    00332 SignalSet::SignalSet(SignalProgram *prog, SignalState state)
    00333     : SignalInstruction(prog, PSO_SET_SIGNAL)
    00334 {
    00335   this->to_state = state;
    00336 }
    00337
    00338 /*virtual*/ void SignalSet::Remove()
    00339 {
    00340   this->next->SetPrevious(this->previous);
    00341   this->previous->SetNext(this->next);
    00342   delete this;
    00343 }
    00344
    00345 /*virtual*/ void SignalSet::Evaluate(SignalVM &vm)
    00346 {
    00347   DEBUG(misc, 7, "  Executing SetSignal, making %s", this->to_state? "green" : "red");
    00348   vm.state       = this->to_state;
    00349   vm.instruction = NULL;
    00350 }
    00351
    00352
    00353 /*virtual*/ void SignalSet::SetNext(SignalInstruction *next_insn)
    00354 {
    00355   this->next = next_insn;
    00356 }
    00357
    00358 static SignalProgram *GetExistingSignalProgram(SignalReference ref)
    00359 {
    00360   ProgramList::iterator i = _signal_programs.find(ref);
    00361   if (i != _signal_programs.end()) {
    00362     assert(i->first == ref);
    00363     return i->second;
    00364   } else {
    00365     return NULL;
    00366   }
    00367 }
    00368
    00369
    00370 SignalProgram *GetSignalProgram(SignalReference ref)
    00371 {
    00372   SignalProgram *pr = GetExistingSignalProgram(ref);
    00373   if (!pr) {
    00374     pr = new SignalProgram(ref.tile, ref.track);
    00375     _signal_programs[ref] = pr;
    00376   } else {
    00377     assert(pr->tile == ref.tile && pr->track == ref.track);
    00378   }
    00379   return pr;
    00380 }
    00381
    00382 uint16 GetSignalSpeed(SignalReference ref)
    00383 {
    00384   SpeedLimits::iterator i = _speedlimits.find(ref);
    00385   if (i != _speedlimits.end()) {
    00386     assert(i->first == ref);
    00387     return i->second;
    00388   } else {
    00389     return 0;
    00390   }
    00391 }
    00392
    00393 void FreeSignalProgram(SignalReference ref)
    00394 {
    00395   DeleteWindowById(WC_SIGNAL_PROGRAM, (ref.tile << 3) | ref.track);
    00396   ProgramList::iterator i = _signal_programs.find(ref);
    00397   if (i != _signal_programs.end()) {
    00398     delete i->second;
    00399     _signal_programs.erase(i);
    00400   }
    00401 }
    00402
    00403 void FreeSignalSpeed(SignalReference ref)
    00404 {
    00405   DeleteWindowById(WC_SIGNAL_PROGRAM, (ref.tile << 3) | ref.track);
    00406   SpeedLimits::iterator i = _speedlimits.find(ref);
    00407   if (i != _speedlimits.end()) {
    00408     _speedlimits.erase(i);
    00409   }
    00410 }
    00411
    00412 void FreeSignalSpeeds()
    00413 {
    00414   _speedlimits.clear();
    00415 }
    00416
    00417 void FreeSignalPrograms()
    00418 {
    00419   ProgramList::iterator i, e;
    00420   for (i = _signal_programs.begin(), e = _signal_programs.end(); i != e;) {
    00421     delete i->second;
    00422     _signal_programs.erase(i++); // Must postincrement here to avoid iterator invalidation.
    00423   }
    00424 }
    00425
    00426 SignalState RunSignalProgram(SignalReference ref, uint num_exits, uint num_green)
    00427 {
    00428   SignalProgram *program = GetSignalProgram(ref);
    00429   SignalVM vm;
    00430   vm.program = program;
    00431   vm.num_exits = num_exits;
    00432   vm.num_green = num_green;
    00433
    00434   vm.instruction = program->first_instruction;
    00435   vm.state = SIGNAL_STATE_RED;
    00436
    00437   DEBUG(misc, 7, "%d exits, of which %d green", vm.num_exits, vm.num_green);
    00438   vm.Execute();
    00439   DEBUG(misc, 7, "Returning %s", vm.state == SIGNAL_STATE_GREEN ? "green" : "red");
    00440   return vm.state;
    00441 }
    00442
    00443 void RemoveProgramDependencies(SignalReference by, SignalReference on)
    00444 {
    00445   SignalProgram *prog = GetSignalProgram(by);
    00446   for (SignalInstruction **b = prog->instructions.Begin(), **i = b, **e = prog->instructions.End();
    00447       i != e; i++) {
    00448     SignalInstruction *insn = *i;
    00449     if (insn->Opcode() == PSO_IF) {
    00450       SignalIf* ifi = static_cast<SignalIf*>(insn);
    00451       if (ifi->condition->ConditionCode() == PSC_SIGNAL_STATE) {
    00452         SignalStateCondition* c = static_cast<SignalStateCondition*>(ifi->condition);
    00453         if(c->sig_tile == by.tile && TrackdirToTrack(c->sig_track) == by.track) {
    00454           c->Invalidate();
    00455         }
    00456       }
    00457     }
    00458   }
    00459
    00460   AddTrackToSignalBuffer(by.tile, by.track, GetTileOwner(by.tile));
    00461   UpdateSignalsInBuffer();
    00462 }
    00463
    00464 void SignalProgram::DebugPrintProgram()
    00465 {
    00466   DEBUG(misc, 5, "Program %p listing", this);
    00467   for (SignalInstruction **b = this->instructions.Begin(), **i = b, **e = this->instructions.End();
    00468       i != e; i++) {
    00469     SignalInstruction *insn = *i;
    00470     DEBUG(misc, 5, " %ld: Opcode %d, prev %d", long(i - b), int(insn->Opcode()),
    00471           int(insn->Previous() ? insn->Previous()->Id() : -1));
    00472   }
    00473 }
    00474
    00489 CommandCost CmdInsertSignalInstruction(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00490 {
    00491   Track track         = Extract<Track,        0,   3>(p1);
    00492   uint instruction_id = GB(p1, 3, 16);
    00493   SignalOpcode op     = Extract<SignalOpcode, 19,  8>(p1);
    00494
    00495   if (!IsValidTrack(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
    00496     return CMD_ERROR;
    00497   }
    00498
    00499   if (!IsTileOwner(tile, _current_company)) {
    00500     return_cmd_error(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
    00501   }
    00502
    00503   SignalProgram *prog = GetExistingSignalProgram(SignalReference(tile, track));
    00504   if (!prog) {
    00505     return_cmd_error(STR_ERR_PROGSIG_NOT_THERE);
    00506   }
    00507
    00508   if (instruction_id > prog->instructions.Length()) {
    00509     return_cmd_error(STR_ERR_PROGSIG_INVALID_INSTRUCTION);
    00510   }
    00511
    00512   bool exec = (flags & DC_EXEC) != 0;
    00513
    00514   SignalInstruction *insert_before = prog->instructions[instruction_id];
    00515   switch (op) {
    00516     case PSO_IF: {
    00517       if (!exec) return CommandCost();
    00518       SignalIf *if_ins = new SignalIf(prog);
    00519       if_ins->Insert(insert_before);
    00520       break;
    00521     }
    00522
    00523     case PSO_SET_SIGNAL: {
    00524       SignalState ss = (SignalState) p2;
    00525       if (ss > SIGNAL_STATE_MAX) return_cmd_error(STR_ERR_PROGSIG_INVALID_OPCODE);
    00526       if (!exec) return CommandCost();
    00527
    00528       SignalSet *set = new SignalSet(prog, ss);
    00529       set->Insert(insert_before);
    00530       break;
    00531     }
    00532
    00533     case PSO_FIRST:
    00534     case PSO_LAST:
    00535     case PSO_IF_ELSE:
    00536     case PSO_IF_ENDIF:
    00537     default:
    00538       return_cmd_error(STR_ERR_PROGSIG_INVALID_OPCODE);
    00539   }
    00540
    00541   if (!exec) return CommandCost();
    00542   AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
    00543   UpdateSignalsInBuffer();
    00544   InvalidateWindowData(WC_SIGNAL_PROGRAM, (tile << 3) | track);
    00545   return CommandCost();
    00546 }
    00547
    00569 CommandCost CmdModifySignalInstruction(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00570 {
    00571   Track track         = Extract<Track, 0, 3 >(p1);
    00572   uint instruction_id = GB(p1, 3, 16);
    00573
    00574   if (!IsValidTrack(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
    00575     return CMD_ERROR;
    00576   }
    00577
    00578   if (!IsTileOwner(tile, _current_company)) {
    00579     return_cmd_error(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
    00580   }
    00581
    00582   SignalProgram *prog = GetExistingSignalProgram(SignalReference(tile, track));
    00583   if (!prog) {
    00584     return_cmd_error(STR_ERR_PROGSIG_NOT_THERE);
    00585   }
    00586
    00587   if (instruction_id > prog->instructions.Length()) {
    00588     return_cmd_error(STR_ERR_PROGSIG_INVALID_INSTRUCTION);
    00589   }
    00590
    00591   bool exec = (flags & DC_EXEC) != 0;
    00592
    00593   SignalInstruction *insn = prog->instructions[instruction_id];
    00594   switch (insn->Opcode()) {
    00595     case PSO_SET_SIGNAL: {
    00596       SignalState state = (SignalState) p2;
    00597       if (state > SIGNAL_STATE_MAX) {
    00598         return_cmd_error(STR_ERR_PROGSIG_INVALID_SIGNAL_STATE);
    00599       }
    00600       if (!exec) {
    00601         return CommandCost();
    00602       }
    00603       SignalSet *ss = static_cast<SignalSet*>(insn);
    00604       ss->to_state = state;
    00605     } break;
    00606
    00607     case PSO_IF: {
    00608       SignalIf *si = static_cast<SignalIf*>(insn);
    00609       byte act = GB(p2, 0, 1);
    00610       if (act == 0) { // Set code.
    00611         SignalConditionCode code = (SignalConditionCode) GB(p2, 1, 8);
    00612         if (code > PSC_MAX) {
    00613           return_cmd_error(STR_ERR_PROGSIG_INVALID_CONDITION);
    00614         }
    00615         if (!exec) return CommandCost();
    00616
    00617         SignalCondition *cond;
    00618         switch (code) {
    00619           case PSC_ALWAYS:
    00620           case PSC_NEVER:
    00621             cond = new SignalSimpleCondition(code);
    00622             break;
    00623
    00624           case PSC_NUM_GREEN:
    00625           case PSC_NUM_RED:
    00626             cond = new SignalVariableCondition(code);
    00627             break;
    00628
    00629           case PSC_SIGNAL_STATE:
    00630             cond = new SignalStateCondition(SignalReference(tile, track), INVALID_TILE, INVALID_TRACKDIR);
    00631             break;
    00632
    00633           default: NOT_REACHED();
    00634         }
    00635         si->SetCondition(cond);
    00636       } else { // Modify condition.
    00637         switch (si->condition->ConditionCode()) {
    00638           case PSC_ALWAYS:
    00639           case PSC_NEVER:
    00640             return CommandCost(STR_ERR_PROGSIG_INVALID_CONDITION_FIELD);
    00641
    00642           case PSC_NUM_GREEN:
    00643           case PSC_NUM_RED: {
    00644             SignalVariableCondition *vc = static_cast<SignalVariableCondition*>(si->condition);
    00645             SignalConditionField f = (SignalConditionField) GB(p2, 1, 2);
    00646             uint32 val = GB(p2, 3, 27);
    00647             if (f == SCF_COMPARATOR) {
    00648               if (val > SGC_LAST) return_cmd_error(STR_ERR_PROGSIG_INVALID_COMPARATOR);
    00649               if (!exec) return CommandCost();
    00650               vc->comparator = (SignalComparator) val;
    00651             } else if (f == SCF_VALUE) {
    00652               if (!exec) return CommandCost();
    00653               vc->value = val;
    00654             } else CommandCost(STR_ERR_PROGSIG_INVALID_CONDITION_FIELD);
    00655           } break;
    00656
    00657           case PSC_SIGNAL_STATE: {
    00658             SignalStateCondition *sc = static_cast<SignalStateCondition*>(si->condition);
    00659             Trackdir  td = (Trackdir)  GB(p2, 1, 4);
    00660             TileIndex ti = (TileIndex) GB(p2, 5, 27);
    00661
    00662             if (!IsValidTile(ti) || !IsValidTrackdir(td) || !HasSignalOnTrackdir(ti, td)
    00663                 || GetTileOwner(ti) != _current_company) {
    00664               return_cmd_error(STR_ERR_PROGSIG_INVALID_SIGNAL);
    00665             }
    00666             if (!exec) return CommandCost();
    00667             sc->SetSignal(ti, td);
    00668           } break;
    00669         }
    00670       }
    00671     } break;
    00672
    00673     case PSO_FIRST:
    00674     case PSO_LAST:
    00675     case PSO_IF_ELSE:
    00676     case PSO_IF_ENDIF:
    00677     default:
    00678       return CommandCost(STR_ERR_PROGSIG_INVALID_OPCODE);
    00679   }
    00680
    00681   if (!exec) return CommandCost();
    00682
    00683   AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
    00684   UpdateSignalsInBuffer();
    00685   InvalidateWindowData(WC_SIGNAL_PROGRAM, (tile << 3) | track);
    00686   return CommandCost();
    00687 }
    00688
    00699 CommandCost CmdRemoveSignalInstruction(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00700 {
    00701   Track track         = Extract<Track, 0, 3 >(p1);
    00702   uint instruction_id = GB(p1, 3, 16);
    00703
    00704   if (!IsValidTrack(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
    00705     return CMD_ERROR;
    00706   }
    00707
    00708   if (!IsTileOwner(tile, _current_company)) {
    00709     return_cmd_error(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
    00710   }
    00711
    00712   SignalProgram *prog = GetExistingSignalProgram(SignalReference(tile, track));
    00713   if (!prog) {
    00714     return_cmd_error(STR_ERR_PROGSIG_NOT_THERE);
    00715   }
    00716
    00717   if (instruction_id > prog->instructions.Length()) {
    00718     return_cmd_error(STR_ERR_PROGSIG_INVALID_INSTRUCTION);
    00719   }
    00720
    00721   bool exec = (flags & DC_EXEC) != 0;
    00722
    00723   SignalInstruction *insn = prog->instructions[instruction_id];
    00724   switch (insn->Opcode()) {
    00725     case PSO_SET_SIGNAL:
    00726     case PSO_IF:
    00727       if (!exec) return CommandCost();
    00728       insn->Remove();
    00729       break;
    00730
    00731     case PSO_FIRST:
    00732     case PSO_LAST:
    00733     case PSO_IF_ELSE:
    00734     case PSO_IF_ENDIF:
    00735     default:
    00736       return_cmd_error(STR_ERR_PROGSIG_INVALID_OPCODE);
    00737   }
    00738
    00739   if (!exec) return CommandCost();
    00740   AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
    00741   UpdateSignalsInBuffer();
    00742   InvalidateWindowData(WC_SIGNAL_PROGRAM, (tile << 3) | track);
    00743   return CommandCost();
    00744 }
    00745
    00755 CommandCost CmdSetSignalSpeedLimit(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00756 {
    00757   Track track         = Extract<Track, 0, 3 >(p1);
    00758
    00759   if (!IsValidTrack(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
    00760     return CMD_ERROR;
    00761   }
    00762
    00763   if (!IsTileOwner(tile, _current_company))
    00764     return_cmd_error(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
    00765
    00766   SignalReference ref = SignalReference(tile, track);
    00767   _speedlimits[ref] = p2;
    00768
    00769   AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
    00770   UpdateSignalsInBuffer();
    00771   InvalidateWindowData(WC_SPEED_SIGNAL, (tile << 3) | track);
    00772   return CommandCost();
    00773 }