mirror of
https://github.com/JGRennison/OpenTTD-patches.git
synced 2024-11-11 13:10:45 +00:00
030e8bf762
This is to make it less unclear what they actually are and do
399 lines
14 KiB
C++
399 lines
14 KiB
C++
/*
|
|
* This file is part of OpenTTD.
|
|
* 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.
|
|
* 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.
|
|
* 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/>.
|
|
*/
|
|
|
|
/** @file programmable_signals.h Programmable Pre-Signals */
|
|
|
|
#ifndef PROGRAMMABLE_SIGNALS_H
|
|
#define PROGRAMMABLE_SIGNALS_H
|
|
#include "rail_map.h"
|
|
#include "core/smallvec_type.hpp"
|
|
#include <map>
|
|
|
|
/** @defgroup progsigs Programmable Pre-Signals */
|
|
///@{
|
|
|
|
/** The Programmable Pre-Signal virtual machine.
|
|
*
|
|
* This structure contains the state of the currently executing signal program.
|
|
*/
|
|
struct SignalVM;
|
|
|
|
class SignalInstruction;
|
|
class SignalSpecial;
|
|
typedef std::vector<SignalInstruction*> InstructionList;
|
|
|
|
enum SignalProgramMgmtCode {
|
|
SPMC_REMOVE, ///< Remove program
|
|
SPMC_CLONE, ///< Clone program
|
|
};
|
|
|
|
/** The actual programmable pre-signal information */
|
|
struct SignalProgram {
|
|
SignalProgram(TileIndex tile, Track track, bool raw = false);
|
|
~SignalProgram();
|
|
void DebugPrintProgram();
|
|
|
|
TileIndex tile;
|
|
Track track;
|
|
|
|
SignalSpecial *first_instruction;
|
|
SignalSpecial *last_instruction;
|
|
InstructionList instructions;
|
|
};
|
|
|
|
/** Programmable Pre-Signal opcode.
|
|
*
|
|
* Opcode types are discriminated by this enumeration. It is primarily used for
|
|
* code which must be able to inspect the type of a signal operation, rather than
|
|
* evaluate it (such as the programming GUI)
|
|
*/
|
|
enum SignalOpcode {
|
|
PSO_FIRST = 0, ///< Start pseudo instruction
|
|
PSO_LAST = 1, ///< End pseudo instruction
|
|
PSO_IF = 2, ///< If instruction
|
|
PSO_IF_ELSE = 3, ///< If Else pseudo instruction
|
|
PSO_IF_ENDIF = 4, ///< If Endif pseudo instruction
|
|
PSO_SET_SIGNAL = 5, ///< Set signal instruction
|
|
|
|
PSO_END,
|
|
PSO_INVALID = 0xFF
|
|
};
|
|
template <> struct EnumPropsT<SignalOpcode> : MakeEnumPropsT<SignalOpcode, byte, PSO_FIRST, PSO_END, PSO_INVALID, 8> {};
|
|
|
|
/** Signal instruction base class. All instructions must derive from this. */
|
|
class SignalInstruction {
|
|
public:
|
|
/// Get the instruction's opcode
|
|
inline SignalOpcode Opcode() const { return this->opcode; }
|
|
|
|
/// Get the previous instruction. If this is nullptr, then this is the first
|
|
/// instruction.
|
|
inline SignalInstruction *Previous() const { return this->previous; }
|
|
|
|
/// Get the Id of this instruction
|
|
inline int Id() const
|
|
// Const cast is safe (perculiarity of SmallVector)
|
|
{ return find_index(program->instructions, const_cast<SignalInstruction*>(this)); }
|
|
|
|
/// Insert this instruction, placing it before @p before_insn
|
|
virtual void Insert(SignalInstruction *before_insn);
|
|
|
|
/// Evaluate the instruction. The instruction should update the VM state.
|
|
virtual void Evaluate(SignalVM &vm) = 0;
|
|
|
|
/// Remove the instruction. When removing itself, an instruction should
|
|
/// <ul>
|
|
/// <li>Set next->previous to previous
|
|
/// <li>Set previous->next to next
|
|
/// <li>Destroy any other children
|
|
/// </ul>
|
|
virtual void Remove() = 0;
|
|
|
|
/// Gets a reference to the previous member. This is only intended for use by
|
|
/// the saveload code.
|
|
inline SignalInstruction *&GetPrevHandle()
|
|
{ return previous; }
|
|
|
|
/// Sets the previous instruction of this instruction. This is only intended
|
|
/// to be used by instructions to update links during insertion and removal.
|
|
inline void SetPrevious(SignalInstruction *prev)
|
|
{ previous = prev; }
|
|
/// Set the next instruction. This is only intended to be used by instructions
|
|
/// to update links during insertion and removal
|
|
virtual void SetNext(SignalInstruction *next_insn) = 0;
|
|
|
|
protected:
|
|
/// Constructs an instruction
|
|
/// @param prog the program to add this instruction to
|
|
/// @param op the opcode of the instruction
|
|
SignalInstruction(SignalProgram *prog, SignalOpcode op) ;
|
|
virtual ~SignalInstruction();
|
|
|
|
const SignalOpcode opcode;
|
|
SignalInstruction *previous;
|
|
SignalProgram *program;
|
|
};
|
|
|
|
/** Programmable Pre-Signal condition code.
|
|
*
|
|
* These discriminate conditions in much the same way that SignalOpcode
|
|
* discriminates instructions.
|
|
*/
|
|
enum SignalConditionCode {
|
|
PSC_ALWAYS = 0, ///< Always true
|
|
PSC_NEVER = 1, ///< Always false
|
|
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_MAX = PSC_SIGNAL_STATE
|
|
};
|
|
|
|
class SignalCondition {
|
|
public:
|
|
/// Get the condition's code
|
|
inline SignalConditionCode ConditionCode() const { return this->cond_code; }
|
|
|
|
/// Evaluate the condition
|
|
virtual bool Evaluate(SignalVM& vm) = 0;
|
|
|
|
/// Destroy the condition. Any children should also be destroyed
|
|
virtual ~SignalCondition();
|
|
|
|
protected:
|
|
SignalCondition(SignalConditionCode code) : cond_code(code) {}
|
|
|
|
const SignalConditionCode cond_code;
|
|
};
|
|
|
|
// -- Condition codes --
|
|
/** Simple condition code. These conditions have no complex inputs, and can be
|
|
* evaluated directly from VM state and their condition code.
|
|
*/
|
|
class SignalSimpleCondition: public SignalCondition {
|
|
public:
|
|
SignalSimpleCondition(SignalConditionCode code);
|
|
virtual bool Evaluate(SignalVM& vm);
|
|
};
|
|
|
|
/** Comparator to use for variable conditions. */
|
|
enum SignalComparator {
|
|
SGC_EQUALS = 0, ///< the variable is equal to the specified value
|
|
SGC_NOT_EQUALS = 1, ///< the variable is not equal to the specified value
|
|
SGC_LESS_THAN = 2, ///< the variable is less than specified value
|
|
SGC_LESS_THAN_EQUALS = 3, ///< the variable is less than or equal to the specified value
|
|
SGC_MORE_THAN = 4, ///< the variable is greater than the specified value
|
|
SGC_MORE_THAN_EQUALS = 5, ///< the variable is grater than or equal to the specified value
|
|
SGC_IS_TRUE = 6, ///< the variable is true (non-zero)
|
|
SGC_IS_FALSE = 7, ///< the variable is false (zero)
|
|
|
|
SGC_LAST = SGC_IS_FALSE
|
|
};
|
|
|
|
/** Which field to modify in a condition. A parameter to CMD_MODIFY_SIGNAL_INSTRUCTION */
|
|
enum SignalConditionField {
|
|
SCF_COMPARATOR = 0, ///< the comparator (value from SignalComparator enum)
|
|
SCF_VALUE = 1, ///< the value (integer value)
|
|
};
|
|
|
|
/** A conditon based upon comparing a variable and a value. This condition can be
|
|
* considered similar to the conditonal jumps in vehicle orders.
|
|
*
|
|
* 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 {
|
|
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);
|
|
};
|
|
|
|
/** A condition which is based upon the state of another signal. */
|
|
class SignalStateCondition: public SignalCondition {
|
|
public:
|
|
SignalStateCondition(SignalReference this_sig, TileIndex sig_tile, Trackdir sig_track);
|
|
|
|
void SetSignal(TileIndex tile, Trackdir track);
|
|
bool IsSignalValid() const;
|
|
bool CheckSignalValid();
|
|
void Invalidate();
|
|
|
|
virtual bool Evaluate(SignalVM& vm);
|
|
virtual ~SignalStateCondition();
|
|
|
|
SignalReference this_sig;
|
|
TileIndex sig_tile;
|
|
Trackdir sig_track;
|
|
};
|
|
|
|
// -- Instructions
|
|
|
|
/** The special start and end pseudo instructions.
|
|
*
|
|
* These instructions serve two purposes:
|
|
* <ol>
|
|
* <li>They permit every other instruction to assume that there is another
|
|
* following it. This makes the code much simpler (and by extension less
|
|
* error prone)</li>
|
|
* <li>Particularly in the case of the End instruction, they provide an
|
|
* instruction in the user interface that can be clicked on to add
|
|
* instructions at the end of a program</li>
|
|
* </ol>
|
|
*/
|
|
class SignalSpecial: public SignalInstruction {
|
|
public:
|
|
/** Constructs a special signal of the opcode @p op in program @p prog.
|
|
*
|
|
* Generally you should not need to call this; it will be called by the
|
|
* program's constructor. An exception is in the saveload code, which needs
|
|
* to construct raw objects to deserialize into
|
|
*/
|
|
SignalSpecial(SignalProgram *prog, SignalOpcode op);
|
|
|
|
/** Evaluates the instruction. If this is an Start instruction, flow will be
|
|
* vectored to the first instruction; if it is an End instruction, the program
|
|
* will terminate and the signal will be left red.
|
|
*/
|
|
virtual void Evaluate(SignalVM &vm);
|
|
|
|
/** Links the first and last instructions in the program. Generally only to be
|
|
* called from the SignalProgram constructor.
|
|
*/
|
|
static void link(SignalSpecial *first, SignalSpecial *last);
|
|
|
|
/** Removes this instruction. If this is the start instruction, then all of
|
|
* the other instructions in the program will be successively removed,
|
|
* (emptying it). If this is the End instruction, then it will do nothing.
|
|
*
|
|
* This operation, unlike when executed on most instructions, does not destroy
|
|
* the instruction.
|
|
*/
|
|
virtual void Remove();
|
|
|
|
/** The next instruction after this one. On the End instruction, this should
|
|
* be nullptr.
|
|
*/
|
|
SignalInstruction *next;
|
|
|
|
virtual void SetNext(SignalInstruction *next_insn);
|
|
};
|
|
|
|
/** If signal instruction. This is perhaps the most important, as without it,
|
|
* programmable pre-signals are pretty useless.
|
|
*
|
|
* It's also the most complex!
|
|
*/
|
|
class SignalIf: public SignalInstruction {
|
|
public:
|
|
/** The If-Else and If-Endif pseudo instructions. The Else instruction
|
|
* follows the Then block, and the Endif instruction follows the Else block.
|
|
*
|
|
* These serve two purposes:
|
|
* <ul>
|
|
* <li>They correctly vector the execution to after the if block
|
|
* (if needed)
|
|
* <li>They provide an instruction for the GUI to insert other instructions
|
|
* before.
|
|
* </ul>
|
|
*/
|
|
class PseudoInstruction: public SignalInstruction {
|
|
public:
|
|
/** Normal constructor. The pseudo instruction will be constructed as
|
|
* belonging to @p block.
|
|
*/
|
|
PseudoInstruction(SignalProgram *prog, SignalIf *block, SignalOpcode op);
|
|
|
|
/** Constructs an empty instruction of type @p op. This should only be used
|
|
* by the saveload code during deserialization. The instruction must have
|
|
* its block field set correctly before the program is run.
|
|
*/
|
|
PseudoInstruction(SignalProgram *prog, SignalOpcode op);
|
|
|
|
/** Removes the pseudo instruction. Unless you are also removing the If it
|
|
* belongs to, this is nonsense and dangerous.
|
|
*/
|
|
virtual void Remove();
|
|
|
|
/** Evaluate the pseudo instruction. This involves vectoring execution to
|
|
* the instruction after the if.
|
|
*/
|
|
virtual void Evaluate(SignalVM &vm);
|
|
|
|
/** The block to which this instruction belongs */
|
|
SignalIf *block;
|
|
virtual void SetNext(SignalInstruction *next_insn);
|
|
};
|
|
|
|
public:
|
|
/** Constructs an If instruction belonging to program @p prog. If @p raw is
|
|
* true, then the instruction is constructed raw (in order for the
|
|
* deserializer to be able to correctly deserialize the instruction).
|
|
*/
|
|
SignalIf(SignalProgram *prog, bool raw = false);
|
|
|
|
/** Sets the instruction's condition, and releases the old condition */
|
|
void SetCondition(SignalCondition *cond);
|
|
|
|
/** Evaluates the If and takes the appropriate branch */
|
|
virtual void Evaluate(SignalVM &vm);
|
|
|
|
virtual void Insert(SignalInstruction *before_insn);
|
|
|
|
/** Removes the If and all of its children */
|
|
virtual void Remove();
|
|
|
|
SignalCondition *condition; ///< The if conditon
|
|
SignalInstruction *if_true; ///< The branch to take if true
|
|
SignalInstruction *if_false; ///< The branch to take if false
|
|
SignalInstruction *after; ///< The branch to take after the If
|
|
|
|
virtual void SetNext(SignalInstruction *next_insn);
|
|
};
|
|
|
|
/** Set signal instruction. This sets the state of the signal and terminates execution */
|
|
class SignalSet: public SignalInstruction {
|
|
public:
|
|
/// Constructs the instruction and sets the state the signal is to be set to
|
|
SignalSet(SignalProgram *prog, SignalState = SIGNAL_STATE_RED);
|
|
|
|
virtual void Evaluate(SignalVM &vm);
|
|
virtual void Remove();
|
|
|
|
/// The state to set the signal to
|
|
SignalState to_state;
|
|
|
|
/// The instruction following this one (for the editor)
|
|
SignalInstruction *next;
|
|
|
|
virtual void SetNext(SignalInstruction *next_insn);
|
|
};
|
|
|
|
/// The map type used for looking up signal programs
|
|
typedef std::map<SignalReference, SignalProgram*> ProgramList;
|
|
|
|
/// The global signal program list
|
|
extern ProgramList _signal_programs;
|
|
|
|
/// Verifies that a SignalReference refers to a signal which has a program.
|
|
static inline bool HasProgrammableSignals(SignalReference ref)
|
|
{
|
|
return IsTileType(ref.tile, MP_RAILWAY) && GetRailTileType(ref.tile) == RAIL_TILE_SIGNALS
|
|
&& IsPresignalProgrammable(ref.tile, ref.track);
|
|
}
|
|
|
|
/// Shows the programming window for the signal identified by @p tile and
|
|
/// @p track.
|
|
void ShowSignalProgramWindow(SignalReference ref);
|
|
|
|
/// Gets the signal program for the tile identified by @p t and @p track.
|
|
/// An empty program will be constructed if none is specified
|
|
SignalProgram *GetSignalProgram(SignalReference ref);
|
|
|
|
SignalProgram *GetExistingSignalProgram(SignalReference ref);
|
|
|
|
/// Frees a signal program by tile and track
|
|
void FreeSignalProgram(SignalReference ref);
|
|
|
|
/// Frees all signal programs (For use when creating a new game)
|
|
void FreeSignalPrograms();
|
|
|
|
/// Runs the signal program, specifying the following parameters.
|
|
SignalState RunSignalProgram(SignalReference ref, uint num_exits, uint num_green);
|
|
|
|
/// Remove dependencies on signal @p on from @p by
|
|
void RemoveProgramDependencies(SignalReference dependency_target, SignalReference signal_to_update);
|
|
///@}
|
|
|
|
#endif
|