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 }