Port of the programmable signals patch to recent trunk
Also add some additional changes from the SpringPP patch, and make some other minor changes/fixes.
This commit is contained in:

committed by
Jonathan G Rennison

parent
0b09a7ac61
commit
fc0efe599e
BIN
bin/data/progsignals.grf
Normal file
BIN
bin/data/progsignals.grf
Normal file
Binary file not shown.
@@ -366,6 +366,8 @@
|
|||||||
<ClCompile Include="..\src\sdl.cpp" />
|
<ClCompile Include="..\src\sdl.cpp" />
|
||||||
<ClCompile Include="..\src\settings.cpp" />
|
<ClCompile Include="..\src\settings.cpp" />
|
||||||
<ClCompile Include="..\src\signal.cpp" />
|
<ClCompile Include="..\src\signal.cpp" />
|
||||||
|
<ClCompile Include="..\src\programmable_signals.cpp" />
|
||||||
|
<ClCompile Include="..\src\programmable_signals_gui.cpp" />
|
||||||
<ClCompile Include="..\src\signs.cpp" />
|
<ClCompile Include="..\src\signs.cpp" />
|
||||||
<ClCompile Include="..\src\sound.cpp" />
|
<ClCompile Include="..\src\sound.cpp" />
|
||||||
<ClCompile Include="..\src\sprite.cpp" />
|
<ClCompile Include="..\src\sprite.cpp" />
|
||||||
@@ -582,6 +584,7 @@
|
|||||||
<ClInclude Include="..\src\ship.h" />
|
<ClInclude Include="..\src\ship.h" />
|
||||||
<ClInclude Include="..\src\signal_func.h" />
|
<ClInclude Include="..\src\signal_func.h" />
|
||||||
<ClInclude Include="..\src\signal_type.h" />
|
<ClInclude Include="..\src\signal_type.h" />
|
||||||
|
<ClInclude Include="..\src\programmable_signals.h" />
|
||||||
<ClInclude Include="..\src\signs_base.h" />
|
<ClInclude Include="..\src\signs_base.h" />
|
||||||
<ClInclude Include="..\src\signs_func.h" />
|
<ClInclude Include="..\src\signs_func.h" />
|
||||||
<ClInclude Include="..\src\signs_type.h" />
|
<ClInclude Include="..\src\signs_type.h" />
|
||||||
@@ -869,6 +872,7 @@
|
|||||||
<ClCompile Include="..\src\saveload\town_sl.cpp" />
|
<ClCompile Include="..\src\saveload\town_sl.cpp" />
|
||||||
<ClCompile Include="..\src\saveload\vehicle_sl.cpp" />
|
<ClCompile Include="..\src\saveload\vehicle_sl.cpp" />
|
||||||
<ClCompile Include="..\src\saveload\waypoint_sl.cpp" />
|
<ClCompile Include="..\src\saveload\waypoint_sl.cpp" />
|
||||||
|
<ClCompile Include="..\src\saveload\signal_sl.cpp" />
|
||||||
<ClInclude Include="..\src\table\airport_defaults.h" />
|
<ClInclude Include="..\src\table\airport_defaults.h" />
|
||||||
<ClInclude Include="..\src\table\airport_movement.h" />
|
<ClInclude Include="..\src\table\airport_movement.h" />
|
||||||
<ClInclude Include="..\src\table\airporttile_ids.h" />
|
<ClInclude Include="..\src\table\airporttile_ids.h" />
|
||||||
|
@@ -327,6 +327,12 @@
|
|||||||
<ClCompile Include="..\src\signal.cpp">
|
<ClCompile Include="..\src\signal.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\programmable_signals.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\programmable_signals_gui.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="..\src\signs.cpp">
|
<ClCompile Include="..\src\signs.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@@ -975,6 +981,9 @@
|
|||||||
<ClInclude Include="..\src\signal_type.h">
|
<ClInclude Include="..\src\signal_type.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\src\programmable_signals.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\src\signs_base.h">
|
<ClInclude Include="..\src\signs_base.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -1836,6 +1845,9 @@
|
|||||||
<ClCompile Include="..\src\saveload\waypoint_sl.cpp">
|
<ClCompile Include="..\src\saveload\waypoint_sl.cpp">
|
||||||
<Filter>Save/Load handlers</Filter>
|
<Filter>Save/Load handlers</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\src\saveload\signal_sl.cpp">
|
||||||
|
<Filter>Save/Load handlers</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClInclude Include="..\src\table\airport_defaults.h">
|
<ClInclude Include="..\src\table\airport_defaults.h">
|
||||||
<Filter>Tables</Filter>
|
<Filter>Tables</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@@ -734,6 +734,14 @@
|
|||||||
RelativePath=".\..\src\signal.cpp"
|
RelativePath=".\..\src\signal.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\..\src\programmable_signals.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\..\src\programmable_signals_gui.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\..\src\signs.cpp"
|
RelativePath=".\..\src\signs.cpp"
|
||||||
>
|
>
|
||||||
@@ -1602,6 +1610,10 @@
|
|||||||
RelativePath=".\..\src\signal_type.h"
|
RelativePath=".\..\src\signal_type.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\..\src\programmable_signals.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\..\src\signs_base.h"
|
RelativePath=".\..\src\signs_base.h"
|
||||||
>
|
>
|
||||||
@@ -2770,6 +2782,10 @@
|
|||||||
RelativePath=".\..\src\saveload\waypoint_sl.cpp"
|
RelativePath=".\..\src\saveload\waypoint_sl.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\..\src\saveload\signal_sl.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter
|
<Filter
|
||||||
Name="Tables"
|
Name="Tables"
|
||||||
|
@@ -731,6 +731,14 @@
|
|||||||
RelativePath=".\..\src\signal.cpp"
|
RelativePath=".\..\src\signal.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\..\src\programmable_signals.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\..\src\programmable_signals_gui.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\..\src\signs.cpp"
|
RelativePath=".\..\src\signs.cpp"
|
||||||
>
|
>
|
||||||
@@ -1599,6 +1607,10 @@
|
|||||||
RelativePath=".\..\src\signal_type.h"
|
RelativePath=".\..\src\signal_type.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\..\src\programmable_signals.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath=".\..\src\signs_base.h"
|
RelativePath=".\..\src\signs_base.h"
|
||||||
>
|
>
|
||||||
@@ -2767,6 +2779,10 @@
|
|||||||
RelativePath=".\..\src\saveload\waypoint_sl.cpp"
|
RelativePath=".\..\src\saveload\waypoint_sl.cpp"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath=".\..\src\saveload\signal_sl.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter
|
<Filter
|
||||||
Name="Tables"
|
Name="Tables"
|
||||||
|
@@ -76,6 +76,8 @@ screenshot.cpp
|
|||||||
#end
|
#end
|
||||||
settings.cpp
|
settings.cpp
|
||||||
signal.cpp
|
signal.cpp
|
||||||
|
programmable_signals.cpp
|
||||||
|
programmable_signals_gui.cpp
|
||||||
signs.cpp
|
signs.cpp
|
||||||
sound.cpp
|
sound.cpp
|
||||||
sprite.cpp
|
sprite.cpp
|
||||||
@@ -321,6 +323,7 @@ settings_type.h
|
|||||||
ship.h
|
ship.h
|
||||||
signal_func.h
|
signal_func.h
|
||||||
signal_type.h
|
signal_type.h
|
||||||
|
programmable_signals.h
|
||||||
signs_base.h
|
signs_base.h
|
||||||
signs_func.h
|
signs_func.h
|
||||||
signs_type.h
|
signs_type.h
|
||||||
@@ -633,6 +636,7 @@ saveload/subsidy_sl.cpp
|
|||||||
saveload/town_sl.cpp
|
saveload/town_sl.cpp
|
||||||
saveload/vehicle_sl.cpp
|
saveload/vehicle_sl.cpp
|
||||||
saveload/waypoint_sl.cpp
|
saveload/waypoint_sl.cpp
|
||||||
|
saveload/signal_sl.cpp
|
||||||
|
|
||||||
# Tables
|
# Tables
|
||||||
table/airport_defaults.h
|
table/airport_defaults.h
|
||||||
|
@@ -198,6 +198,10 @@ CommandProc CmdSetTimetableStart;
|
|||||||
|
|
||||||
CommandProc CmdOpenCloseAirport;
|
CommandProc CmdOpenCloseAirport;
|
||||||
|
|
||||||
|
CommandProc CmdInsertSignalInstruction;
|
||||||
|
CommandProc CmdModifySignalInstruction;
|
||||||
|
CommandProc CmdRemoveSignalInstruction;
|
||||||
|
|
||||||
#define DEF_CMD(proc, flags, type) {proc, #proc, (CommandFlags)flags, type}
|
#define DEF_CMD(proc, flags, type) {proc, #proc, (CommandFlags)flags, type}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -354,6 +358,10 @@ static const Command _command_proc_table[] = {
|
|||||||
DEF_CMD(CmdSetTimetableStart, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_TIMETABLE_START
|
DEF_CMD(CmdSetTimetableStart, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SET_TIMETABLE_START
|
||||||
|
|
||||||
DEF_CMD(CmdOpenCloseAirport, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_OPEN_CLOSE_AIRPORT
|
DEF_CMD(CmdOpenCloseAirport, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_OPEN_CLOSE_AIRPORT
|
||||||
|
|
||||||
|
DEF_CMD(CmdInsertSignalInstruction, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_INSERT_SIGNAL_INSTRUCTION
|
||||||
|
DEF_CMD(CmdModifySignalInstruction, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_MODIFY_SIGNAL_INSTRUCTION
|
||||||
|
DEF_CMD(CmdRemoveSignalInstruction, 0, CMDT_LANDSCAPE_CONSTRUCTION), // CMD_REMOVE_SIGNAL_INSTRUCTION
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@@ -329,6 +329,10 @@ enum Commands {
|
|||||||
|
|
||||||
CMD_OPEN_CLOSE_AIRPORT, ///< open/close an airport to incoming aircraft
|
CMD_OPEN_CLOSE_AIRPORT, ///< open/close an airport to incoming aircraft
|
||||||
|
|
||||||
|
CMD_INSERT_SIGNAL_INSTRUCTION, ///< insert a signal instruction
|
||||||
|
CMD_MODIFY_SIGNAL_INSTRUCTION, ///< modifies a signal instruction
|
||||||
|
CMD_REMOVE_SIGNAL_INSTRUCTION, ///< removes a signal instruction
|
||||||
|
|
||||||
CMD_END, ///< Must ALWAYS be on the end of this list!! (period)
|
CMD_END, ///< Must ALWAYS be on the end of this list!! (period)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -170,6 +170,9 @@ static void LoadSpriteTables()
|
|||||||
_palette_remap_grf[i] = (PAL_DOS != used_set->palette);
|
_palette_remap_grf[i] = (PAL_DOS != used_set->palette);
|
||||||
LoadGrfFile(used_set->files[GFT_BASE].filename, 0, i++);
|
LoadGrfFile(used_set->files[GFT_BASE].filename, 0, i++);
|
||||||
|
|
||||||
|
/* Progsignal sprites. */
|
||||||
|
LoadGrfFile("progsignals.grf", SPR_PROGSIGNAL_BASE, i++);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The second basic file always starts at the given location and does
|
* The second basic file always starts at the given location and does
|
||||||
* contain a different amount of sprites depending on the "type"; DOS
|
* contain a different amount of sprites depending on the "type"; DOS
|
||||||
|
@@ -1292,6 +1292,8 @@ STR_CONFIG_SETTING_LANDSCAPE :Landscape: {STR
|
|||||||
STR_CONFIG_SETTING_LANDSCAPE_HELPTEXT :Landscapes define basic gameplay scenarios with different cargos and town growth requirements. NewGRF and Game Scripts allow finer control though
|
STR_CONFIG_SETTING_LANDSCAPE_HELPTEXT :Landscapes define basic gameplay scenarios with different cargos and town growth requirements. NewGRF and Game Scripts allow finer control though
|
||||||
STR_CONFIG_SETTING_LAND_GENERATOR :Land generator: {STRING2}
|
STR_CONFIG_SETTING_LAND_GENERATOR :Land generator: {STRING2}
|
||||||
STR_CONFIG_SETTING_LAND_GENERATOR_HELPTEXT :The original generator depends on the base graphics set, and composes fixed landscape shapes. TerraGenesis is a Perlin noise based generator with finer control settings
|
STR_CONFIG_SETTING_LAND_GENERATOR_HELPTEXT :The original generator depends on the base graphics set, and composes fixed landscape shapes. TerraGenesis is a Perlin noise based generator with finer control settings
|
||||||
|
STR_CONFIG_SETTING_MAX_SIGNAL_EVALUATIONS :Maximum number of programmable signal changes permitted at once: {STRING2}
|
||||||
|
STR_CONFIG_SETTING_MAX_SIGNAL_EVALUATIONS_HELPTEXT :Sets the maximum number of programmable signal changes permitted at once
|
||||||
STR_CONFIG_SETTING_LAND_GENERATOR_ORIGINAL :Original
|
STR_CONFIG_SETTING_LAND_GENERATOR_ORIGINAL :Original
|
||||||
STR_CONFIG_SETTING_LAND_GENERATOR_TERRA_GENESIS :TerraGenesis
|
STR_CONFIG_SETTING_LAND_GENERATOR_TERRA_GENESIS :TerraGenesis
|
||||||
STR_CONFIG_SETTING_TERRAIN_TYPE :Terrain type: {STRING2}
|
STR_CONFIG_SETTING_TERRAIN_TYPE :Terrain type: {STRING2}
|
||||||
@@ -2357,12 +2359,14 @@ STR_BUILD_SIGNAL_SEMAPHORE_NORM_TOOLTIP :{BLACK}Block Si
|
|||||||
STR_BUILD_SIGNAL_SEMAPHORE_ENTRY_TOOLTIP :{BLACK}Entry Signal (semaphore){}Green as long as there is one or more green exit-signal from the following section of track. Otherwise it shows red
|
STR_BUILD_SIGNAL_SEMAPHORE_ENTRY_TOOLTIP :{BLACK}Entry Signal (semaphore){}Green as long as there is one or more green exit-signal from the following section of track. Otherwise it shows red
|
||||||
STR_BUILD_SIGNAL_SEMAPHORE_EXIT_TOOLTIP :{BLACK}Exit Signal (semaphore){}Behaves in the same way as a block signal but is necessary to trigger the correct colour on entry & combo pre-signals
|
STR_BUILD_SIGNAL_SEMAPHORE_EXIT_TOOLTIP :{BLACK}Exit Signal (semaphore){}Behaves in the same way as a block signal but is necessary to trigger the correct colour on entry & combo pre-signals
|
||||||
STR_BUILD_SIGNAL_SEMAPHORE_COMBO_TOOLTIP :{BLACK}Combo Signal (semaphore){}The combo signal simply acts as both an entry and exit signal. This allows you to build large "trees" of pre-signals
|
STR_BUILD_SIGNAL_SEMAPHORE_COMBO_TOOLTIP :{BLACK}Combo Signal (semaphore){}The combo signal simply acts as both an entry and exit signal. This allows you to build large "trees" of pre-signals
|
||||||
|
STR_BUILD_SIGNAL_SEMAPHORE_PROG_TOOLTIP :{BLACK}Programmable-Signal (semaphore){}The programmable signal is a combo-signal which can be programmed to behave in complex ways.
|
||||||
STR_BUILD_SIGNAL_SEMAPHORE_PBS_TOOLTIP :{BLACK}Path Signal (semaphore){}A path signal allows more than one train to enter a signal block at the same time, if the train can reserve a path to a safe stopping point. Standard path signals can be passed from the back side
|
STR_BUILD_SIGNAL_SEMAPHORE_PBS_TOOLTIP :{BLACK}Path Signal (semaphore){}A path signal allows more than one train to enter a signal block at the same time, if the train can reserve a path to a safe stopping point. Standard path signals can be passed from the back side
|
||||||
STR_BUILD_SIGNAL_SEMAPHORE_PBS_OWAY_TOOLTIP :{BLACK}One-way Path Signal (semaphore){}A path signal allows more than one train to enter a signal block at the same time, if the train can reserve a path to a safe stopping point. One-way path signals can't be passed from the back side
|
STR_BUILD_SIGNAL_SEMAPHORE_PBS_OWAY_TOOLTIP :{BLACK}One-way Path Signal (semaphore){}A path signal allows more than one train to enter a signal block at the same time, if the train can reserve a path to a safe stopping point. One-way path signals can't be passed from the back side
|
||||||
STR_BUILD_SIGNAL_ELECTRIC_NORM_TOOLTIP :{BLACK}Block Signal (electric){}This is the most basic type of signal, allowing only one train to be in the same block at the same time
|
STR_BUILD_SIGNAL_ELECTRIC_NORM_TOOLTIP :{BLACK}Block Signal (electric){}This is the most basic type of signal, allowing only one train to be in the same block at the same time
|
||||||
STR_BUILD_SIGNAL_ELECTRIC_ENTRY_TOOLTIP :{BLACK}Entry Signal (electric){}Green as long as there is one or more green exit-signal from the following section of track. Otherwise it shows red
|
STR_BUILD_SIGNAL_ELECTRIC_ENTRY_TOOLTIP :{BLACK}Entry Signal (electric){}Green as long as there is one or more green exit-signal from the following section of track. Otherwise it shows red
|
||||||
STR_BUILD_SIGNAL_ELECTRIC_EXIT_TOOLTIP :{BLACK}Exit Signal (electric){}Behaves in the same way as a block signal but is necessary to trigger the correct colour on entry & combo pre-signals
|
STR_BUILD_SIGNAL_ELECTRIC_EXIT_TOOLTIP :{BLACK}Exit Signal (electric){}Behaves in the same way as a block signal but is necessary to trigger the correct colour on entry & combo pre-signals
|
||||||
STR_BUILD_SIGNAL_ELECTRIC_COMBO_TOOLTIP :{BLACK}Combo Signal (electric){}The combo signal simply acts as both an entry and exit signal. This allows you to build large "trees" of pre-signals
|
STR_BUILD_SIGNAL_ELECTRIC_COMBO_TOOLTIP :{BLACK}Combo Signal (electric){}The combo signal simply acts as both an entry and exit signal. This allows you to build large "trees" of pre-signals
|
||||||
|
STR_BUILD_SIGNAL_ELECTRIC_PROG_TOOLTIP :{BLACK}Programmable-Signal (electric){}The programmable signal is a combo-signal which can be programmed to behave in complex ways.
|
||||||
STR_BUILD_SIGNAL_ELECTRIC_PBS_TOOLTIP :{BLACK}Path Signal (electric){}A path signal allows more than one train to enter a signal block at the same time, if the train can reserve a path to a safe stopping point. Standard path signals can be passed from the back side
|
STR_BUILD_SIGNAL_ELECTRIC_PBS_TOOLTIP :{BLACK}Path Signal (electric){}A path signal allows more than one train to enter a signal block at the same time, if the train can reserve a path to a safe stopping point. Standard path signals can be passed from the back side
|
||||||
STR_BUILD_SIGNAL_ELECTRIC_PBS_OWAY_TOOLTIP :{BLACK}One-way Path Signal (electric){}A path signal allows more than one train to enter a signal block at the same time, if the train can reserve a path to a safe stopping point. One-way path signals can't be passed from the back side
|
STR_BUILD_SIGNAL_ELECTRIC_PBS_OWAY_TOOLTIP :{BLACK}One-way Path Signal (electric){}A path signal allows more than one train to enter a signal block at the same time, if the train can reserve a path to a safe stopping point. One-way path signals can't be passed from the back side
|
||||||
STR_BUILD_SIGNAL_CONVERT_TOOLTIP :{BLACK}Signal Convert{}When selected, clicking an existing signal will convert it to the selected signal type and variant. Ctrl+Click will toggle the existing variant. Shift+Click shows estimated conversion cost
|
STR_BUILD_SIGNAL_CONVERT_TOOLTIP :{BLACK}Signal Convert{}When selected, clicking an existing signal will convert it to the selected signal type and variant. Ctrl+Click will toggle the existing variant. Shift+Click shows estimated conversion cost
|
||||||
@@ -2370,6 +2374,70 @@ STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_TOOLTIP :{BLACK}Dragging
|
|||||||
STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_DECREASE_TOOLTIP :{BLACK}Decrease dragging signal density
|
STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_DECREASE_TOOLTIP :{BLACK}Decrease dragging signal density
|
||||||
STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_INCREASE_TOOLTIP :{BLACK}Increase dragging signal density
|
STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_INCREASE_TOOLTIP :{BLACK}Increase dragging signal density
|
||||||
|
|
||||||
|
# Programmable Signals
|
||||||
|
STR_PROGRAM_SIGNAL_TOOLTIP :{BLACK}Program signal
|
||||||
|
|
||||||
|
STR_ERR_PROGSIG_INVALID_INSTRUCTION :{WHITE}Cannot insert instruction after instruction with invalid ID
|
||||||
|
STR_ERR_PROGSIG_INVALID_OPCODE :{WHITE}Cannot insert an instruction of that opcode
|
||||||
|
STR_ERR_PROGSIG_NOT_THERE :{WHITE}There is no programmable signal there
|
||||||
|
STR_ERR_PROGSIG_INVALID_SIGNAL_STATE :{WHITE}That signal state is invalid
|
||||||
|
STR_ERR_PROGSIG_INVALID_CONDITION :{WHITE}That condition is invalid
|
||||||
|
STR_ERR_PROGSIG_INVALID_CONDITION_FIELD :{WHITE}That field is not valid for the condition
|
||||||
|
STR_ERR_PROGSIG_INVALID_COMPARATOR :{WHITE}That comparator is not valid
|
||||||
|
STR_ERR_PROGSIG_INVALID_SIGNAL :{WHITE}Invalid signal selected
|
||||||
|
|
||||||
|
STR_PROGSIG_CAPTION :{WHITE}Signal Program
|
||||||
|
STR_PROGSIG_COND_VARIABLE_TOOLTIP :{BLACK}Condition to compare upon
|
||||||
|
STR_PROGSIG_COND_COMPARATOR_TOOLTIP :{BLACK}Operator to use to compare variable
|
||||||
|
STR_PROGSIG_COND_VALUE_TOOLTIP :{BLACK}Value to compare variable against
|
||||||
|
STR_PROGSIG_SIGNAL_STATE_TOOLTIP :{BLACK}Set signal to state
|
||||||
|
STR_PROGSIG_COND_SET_SIGNAL :{BLACK}Set signal
|
||||||
|
STR_PROGSIG_COND_SET_SIGNAL_TOOLTIP :{BLACK}Set the signal to be looked at
|
||||||
|
STR_PROGSIG_GOTO_SIGNAL :{BLACK}Goto signal
|
||||||
|
STR_PROGSIG_GOTO_SIGNAL_TOOLTIP :{BLACK}Go to this signal
|
||||||
|
STR_PROGSIG_INSERT_TOOLTIP :{BLACK}Insert an instruction
|
||||||
|
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_REMOVE_PROGRAM :{RED}Remove program
|
||||||
|
STR_PROGSIG_COPY_PROGRAM :{BLUE}Copy program
|
||||||
|
STR_PROGSIG_REMOVE :{BLACK}Remove
|
||||||
|
STR_PROGSIG_INSERT :Insert
|
||||||
|
STR_PROGSIG_INSERT_IF :Condition
|
||||||
|
STR_PROGSIG_INSERT_SET_SIGNAL :Set signal state
|
||||||
|
|
||||||
|
STR_PROGSIG_FIRST :Start
|
||||||
|
STR_PROGSIG_LAST :End
|
||||||
|
|
||||||
|
STR_PROGSIG_IF :If {RAW_STRING} Then
|
||||||
|
STR_PROGSIG_ELSE :Else
|
||||||
|
STR_PROGSIG_ENDIF :End If
|
||||||
|
|
||||||
|
STR_PROGSIG_SET_SIGNAL :Make signal {STRING}
|
||||||
|
|
||||||
|
STR_PROGSIG_COND_ALWAYS :always
|
||||||
|
STR_PROGSIG_COND_NEVER :never
|
||||||
|
STR_PROGSIG_COND_COMPARE :{STRING} {STRING} {NUM}
|
||||||
|
STR_PROGSIG_COND_SIGNAL_STATE :signal state
|
||||||
|
STR_PROGSIG_CONDVAR_SIGNAL_STATE :{STRING1} is green
|
||||||
|
STR_PROGSIG_CONDVAR_SIGNAL_STATE_SPECIFIED :specified signal
|
||||||
|
STR_PROGSIG_CONDVAR_SIGNAL_STATE_UNSPECIFIED :{RED}unspecified signal{STRING}
|
||||||
|
STR_PROGSIG_CONDVAR_NUM_RED :red signals
|
||||||
|
STR_PROGSIG_CONDVAR_NUM_GREEN :green signals
|
||||||
|
|
||||||
|
STR_PROGSIG_CONDITION_VALUE_CAPT :{WHITE}Condition value
|
||||||
|
|
||||||
|
STR_ERROR_CAN_T_INSERT_INSTRUCTION :{WHITE}Can't insert instruction
|
||||||
|
STR_ERROR_CAN_T_MODIFY_INSTRUCTION :{WHITE}Can't modify instruction
|
||||||
|
STR_ERROR_CAN_T_REMOVE_INSTRUCTION :{WHITE}Can't remove instruction
|
||||||
|
STR_ERROR_CAN_T_GOTO_UNDEFINED_SIGNAL :{WHITE}Can't go to undefined signal
|
||||||
|
STR_ERROR_NOT_AN_EXIT_SIGNAL :{WHITE}Not an exit signal
|
||||||
|
STR_ERROR_NOT_AN_PROG_SIGNAL :{WHITE}Not an programmable signal
|
||||||
|
STR_ERROR_CANNOT_USE_SELF :{WHITE}Can't copy program from myself
|
||||||
|
STR_ERROR_CAN_T_DEPEND_UPON_BIDIRECTIONAL_SIGNALS :{WHITE}Cannot conditionally depend upon bidirectional signals
|
||||||
|
STR_ERROR_INVALID_SIGNAL :{WHITE}Invalid signal
|
||||||
|
|
||||||
# Bridge selection window
|
# Bridge selection window
|
||||||
STR_SELECT_RAIL_BRIDGE_CAPTION :{WHITE}Select Rail Bridge
|
STR_SELECT_RAIL_BRIDGE_CAPTION :{WHITE}Select Rail Bridge
|
||||||
STR_SELECT_ROAD_BRIDGE_CAPTION :{WHITE}Select Road Bridge
|
STR_SELECT_ROAD_BRIDGE_CAPTION :{WHITE}Select Road Bridge
|
||||||
@@ -2606,23 +2674,30 @@ STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_SIGNALS :{STRING} track
|
|||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRESIGNALS :{STRING} track with pre-signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRESIGNALS :{STRING} track with pre-signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXITSIGNALS :{STRING} track with exit-signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXITSIGNALS :{STRING} track with exit-signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBOSIGNALS :{STRING} track with combo-signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBOSIGNALS :{STRING} track with combo-signals
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PROGSIGNALS :{STRING} track with programmable signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBSSIGNALS :{STRING} track with path signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBSSIGNALS :{STRING} track with path signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NOENTRYSIGNALS :{STRING} track with one-way path signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NOENTRYSIGNALS :{STRING} track with one-way path signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS :{STRING} track with block and pre-signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS :{STRING} track with block and pre-signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS :{STRING} track with block and exit-signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS :{STRING} track with block and exit-signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS :{STRING} track with block and combo-signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS :{STRING} track with block and combo-signals
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PROGSIGNALS :{STRING} track with block and programmable signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS :{STRING} track with block and path signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS :{STRING} track with block and path signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS :{STRING} track with block and one-way path signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS :{STRING} track with block and one-way path signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS :{STRING} track with pre- and exit-signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS :{STRING} track with pre- and exit-signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS :{STRING} track with pre- and combo-signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS :{STRING} track with pre- and combo-signals
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PROGSIGNALS :{STRING} track with pre- and programmable signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS :{STRING} track with pre- and path signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS :{STRING} track with pre- and path signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS :{STRING} track with pre- and one-way path signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS :{STRING} track with pre- and one-way path signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS :{STRING} track with exit- and combo-signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS :{STRING} track with exit- and combo-signals
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PROGSIGNALS :{STRING} track with exit- and programmable signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS :{STRING} track with exit- and path signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS :{STRING} track with exit- and path signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS :{STRING} track with exit- and one-way path signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS :{STRING} track with exit- and one-way path signals
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PROGSIGNALS :{STRING} track with combo- and programmable signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS :{STRING} track with combo- and path signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS :{STRING} track with combo- and path signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS :{STRING} track with combo- and one-way path signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS :{STRING} track with combo- and one-way path signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS :{STRING} track with path and one-way path signals
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS :{STRING} track with path and one-way path signals
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_PROGSIGNALS :{STRING} track with path and programmable signals
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NOENTRY_PROGSIGNALS :{STRING} track with one-way path and programmable signals
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT :{STRING} train depot
|
STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT :{STRING} train depot
|
||||||
|
|
||||||
STR_LAI_ROAD_DESCRIPTION_ROAD :Road
|
STR_LAI_ROAD_DESCRIPTION_ROAD :Road
|
||||||
@@ -4300,6 +4375,8 @@ STR_ERROR_THERE_ARE_NO_SIGNALS :{WHITE}... ther
|
|||||||
|
|
||||||
STR_ERROR_CAN_T_CONVERT_RAIL :{WHITE}Can't convert rail type here...
|
STR_ERROR_CAN_T_CONVERT_RAIL :{WHITE}Can't convert rail type here...
|
||||||
|
|
||||||
|
STR_ERROR_SIGNAL_CHANGES :{WHITE}Number of programmable signal evaluations exceeded limit
|
||||||
|
|
||||||
# Road construction errors
|
# Road construction errors
|
||||||
STR_ERROR_MUST_REMOVE_ROAD_FIRST :{WHITE}Must remove road first
|
STR_ERROR_MUST_REMOVE_ROAD_FIRST :{WHITE}Must remove road first
|
||||||
STR_ERROR_ONEWAY_ROADS_CAN_T_HAVE_JUNCTION :{WHITE}... one way roads can't have junctions
|
STR_ERROR_ONEWAY_ROADS_CAN_T_HAVE_JUNCTION :{WHITE}... one way roads can't have junctions
|
||||||
@@ -4953,6 +5030,8 @@ STR_TINY_BLACK_HEIGHT :{TINY_FONT}{BLA
|
|||||||
STR_TINY_BLACK_VEHICLE :{TINY_FONT}{BLACK}{VEHICLE}
|
STR_TINY_BLACK_VEHICLE :{TINY_FONT}{BLACK}{VEHICLE}
|
||||||
STR_TINY_RIGHT_ARROW :{TINY_FONT}{RIGHT_ARROW}
|
STR_TINY_RIGHT_ARROW :{TINY_FONT}{RIGHT_ARROW}
|
||||||
|
|
||||||
|
STR_WHITE :{WHITE}
|
||||||
|
STR_BLACK :{BLACK}
|
||||||
STR_BLACK_1 :{BLACK}1
|
STR_BLACK_1 :{BLACK}1
|
||||||
STR_BLACK_2 :{BLACK}2
|
STR_BLACK_2 :{BLACK}2
|
||||||
STR_BLACK_3 :{BLACK}3
|
STR_BLACK_3 :{BLACK}3
|
||||||
|
@@ -63,6 +63,7 @@
|
|||||||
#include "subsidy_func.h"
|
#include "subsidy_func.h"
|
||||||
#include "gfx_layout.h"
|
#include "gfx_layout.h"
|
||||||
#include "viewport_sprite_sorter.h"
|
#include "viewport_sprite_sorter.h"
|
||||||
|
#include "programmable_signals.h"
|
||||||
|
|
||||||
#include "linkgraph/linkgraphschedule.h"
|
#include "linkgraph/linkgraphschedule.h"
|
||||||
|
|
||||||
@@ -304,6 +305,9 @@ static void ShutdownGame()
|
|||||||
LinkGraphSchedule::Clear();
|
LinkGraphSchedule::Clear();
|
||||||
PoolBase::Clean(PT_ALL);
|
PoolBase::Clean(PT_ALL);
|
||||||
|
|
||||||
|
FreeSignalPrograms();
|
||||||
|
FreeSignalDependencies();
|
||||||
|
|
||||||
/* No NewGRFs were loaded when it was still bootstrapping. */
|
/* No NewGRFs were loaded when it was still bootstrapping. */
|
||||||
if (_game_mode != GM_BOOTSTRAP) ResetNewGRFData();
|
if (_game_mode != GM_BOOTSTRAP) ResetNewGRFData();
|
||||||
|
|
||||||
|
@@ -230,6 +230,7 @@ public:
|
|||||||
/* special signal penalties */
|
/* special signal penalties */
|
||||||
if (n.m_num_signals_passed == 0) {
|
if (n.m_num_signals_passed == 0) {
|
||||||
switch (sig_type) {
|
switch (sig_type) {
|
||||||
|
case SIGTYPE_PROG:
|
||||||
case SIGTYPE_COMBO:
|
case SIGTYPE_COMBO:
|
||||||
case SIGTYPE_EXIT: cost += Yapf().PfGetSettings().rail_firstred_exit_penalty; break; // first signal is red pre-signal-exit
|
case SIGTYPE_EXIT: cost += Yapf().PfGetSettings().rail_firstred_exit_penalty; break; // first signal is red pre-signal-exit
|
||||||
case SIGTYPE_NORMAL:
|
case SIGTYPE_NORMAL:
|
||||||
|
699
src/programmable_signals.cpp
Normal file
699
src/programmable_signals.cpp
Normal file
@@ -0,0 +1,699 @@
|
|||||||
|
/* $Id$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.cpp Programmable Signals */
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "programmable_signals.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "command_func.h"
|
||||||
|
#include "table/strings.h"
|
||||||
|
#include "window_func.h"
|
||||||
|
#include "company_func.h"
|
||||||
|
#include "cmd_helper.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
ProgramList _signal_programs;
|
||||||
|
|
||||||
|
SignalProgram::SignalProgram(TileIndex tile, Track track, bool raw)
|
||||||
|
{
|
||||||
|
this->tile = tile;
|
||||||
|
this->track = track;
|
||||||
|
if (!raw) {
|
||||||
|
this->first_instruction = new SignalSpecial(this, PSO_FIRST);
|
||||||
|
this->last_instruction = new SignalSpecial(this, PSO_LAST);
|
||||||
|
SignalSpecial::link(this->first_instruction, this->last_instruction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalProgram::~SignalProgram()
|
||||||
|
{
|
||||||
|
this->DebugPrintProgram();
|
||||||
|
this->first_instruction->Remove();
|
||||||
|
delete this->first_instruction;
|
||||||
|
delete this->last_instruction;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SignalVM {
|
||||||
|
// Initial information
|
||||||
|
uint num_exits; ///< Number of exits from block
|
||||||
|
uint num_green; ///< Number of green exits from block
|
||||||
|
SignalProgram *program; ///< The program being run
|
||||||
|
|
||||||
|
// Current state
|
||||||
|
SignalInstruction *instruction; ///< Instruction to execute next
|
||||||
|
|
||||||
|
// Output state
|
||||||
|
SignalState state;
|
||||||
|
|
||||||
|
void Execute()
|
||||||
|
{
|
||||||
|
DEBUG(misc, 6, "Begining execution of programmable signal on tile %x, track %d",
|
||||||
|
this->program->tile, this->program->track);
|
||||||
|
do {
|
||||||
|
DEBUG(misc, 10, " Executing instruction %d, opcode %d", this->instruction->Id(), this->instruction->Opcode());
|
||||||
|
this->instruction->Evaluate(*this);
|
||||||
|
} while (this->instruction);
|
||||||
|
|
||||||
|
DEBUG(misc, 6, "Completed");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// -- Conditions
|
||||||
|
|
||||||
|
SignalCondition::~SignalCondition()
|
||||||
|
{}
|
||||||
|
|
||||||
|
SignalSimpleCondition::SignalSimpleCondition(SignalConditionCode code)
|
||||||
|
: SignalCondition(code)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/* virtual */ bool SignalSimpleCondition::Evaluate(SignalVM &vm)
|
||||||
|
{
|
||||||
|
switch (this->cond_code) {
|
||||||
|
case PSC_ALWAYS: return true;
|
||||||
|
case PSC_NEVER: return false;
|
||||||
|
default: NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalVariableCondition::SignalVariableCondition(SignalConditionCode code)
|
||||||
|
: SignalCondition(code)
|
||||||
|
{
|
||||||
|
switch (this->cond_code) {
|
||||||
|
case PSC_NUM_GREEN: comparator = SGC_NOT_EQUALS; break;
|
||||||
|
case PSC_NUM_RED: comparator = SGC_EQUALS; break;
|
||||||
|
default: NOT_REACHED();
|
||||||
|
}
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ bool SignalVariableCondition::Evaluate(SignalVM &vm)
|
||||||
|
{
|
||||||
|
uint32 var_val;
|
||||||
|
switch (this->cond_code) {
|
||||||
|
case PSC_NUM_GREEN: var_val = vm.num_green; break;
|
||||||
|
case PSC_NUM_RED: var_val = vm.num_exits - vm.num_green; break;
|
||||||
|
default: NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalStateCondition::SignalStateCondition(SignalReference this_sig,
|
||||||
|
TileIndex sig_tile, Trackdir sig_track)
|
||||||
|
: SignalCondition(PSC_SIGNAL_STATE), this_sig(this_sig), sig_tile(sig_tile)
|
||||||
|
, sig_track(sig_track)
|
||||||
|
{
|
||||||
|
if (this->IsSignalValid())
|
||||||
|
AddSignalDependency(SignalReference(this->sig_tile, TrackdirToTrack(sig_track)),
|
||||||
|
this->this_sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SignalStateCondition::IsSignalValid()
|
||||||
|
{
|
||||||
|
if (IsValidTile(this->sig_tile)) {
|
||||||
|
if (HasSignalOnTrackdir(this->sig_tile, this->sig_track)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SignalStateCondition::Invalidate()
|
||||||
|
{
|
||||||
|
this->sig_tile = INVALID_TILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SignalStateCondition::SetSignal(TileIndex tile, Trackdir track)
|
||||||
|
{
|
||||||
|
if (this->IsSignalValid())
|
||||||
|
RemoveSignalDependency(SignalReference(this->sig_tile, TrackdirToTrack(sig_track)),
|
||||||
|
this->this_sig);
|
||||||
|
this->sig_tile = tile;
|
||||||
|
this->sig_track = track;
|
||||||
|
if (this->IsSignalValid())
|
||||||
|
AddSignalDependency(SignalReference(this->sig_tile, TrackdirToTrack(sig_track)),
|
||||||
|
this->this_sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ SignalStateCondition::~SignalStateCondition()
|
||||||
|
{
|
||||||
|
if (this->IsSignalValid())
|
||||||
|
RemoveSignalDependency(SignalReference(this->sig_tile, TrackdirToTrack(sig_track)),
|
||||||
|
this->this_sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ bool SignalStateCondition::Evaluate(SignalVM& vm)
|
||||||
|
{
|
||||||
|
if (!this->IsSignalValid()) {
|
||||||
|
DEBUG(misc, 1, "Signal (%x, %d) has an invalid condition", this->this_sig.tile, this->this_sig.track);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetSignalStateByTrackdir(this->sig_tile, this->sig_track) == SIGNAL_STATE_GREEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- Instructions
|
||||||
|
SignalInstruction::SignalInstruction(SignalProgram *prog, SignalOpcode op)
|
||||||
|
: opcode(op), previous(NULL), program(prog)
|
||||||
|
{
|
||||||
|
*program->instructions.Append() = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalInstruction::~SignalInstruction()
|
||||||
|
{
|
||||||
|
SignalInstruction** pthis = program->instructions.Find(this);
|
||||||
|
assert(pthis != program->instructions.End());
|
||||||
|
program->instructions.Erase(pthis);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SignalInstruction::Insert(SignalInstruction *before_insn)
|
||||||
|
{
|
||||||
|
this->previous = before_insn->Previous();
|
||||||
|
before_insn->Previous()->SetNext(this);
|
||||||
|
before_insn->SetPrevious(this);
|
||||||
|
this->SetNext(before_insn);
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalSpecial::SignalSpecial(SignalProgram *prog, SignalOpcode op)
|
||||||
|
: SignalInstruction(prog, op)
|
||||||
|
{
|
||||||
|
assert(op == PSO_FIRST || op == PSO_LAST);
|
||||||
|
this->next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ void SignalSpecial::Remove()
|
||||||
|
{
|
||||||
|
if (opcode == PSO_FIRST) {
|
||||||
|
while (this->next->Opcode() != PSO_LAST) this->next->Remove();
|
||||||
|
} else if (opcode == PSO_LAST) {
|
||||||
|
} else NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*static*/ void SignalSpecial::link(SignalSpecial *first, SignalSpecial *last)
|
||||||
|
{
|
||||||
|
assert(first->opcode == PSO_FIRST && last->opcode == PSO_LAST);
|
||||||
|
first->next = last;
|
||||||
|
last->previous = first;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SignalSpecial::Evaluate(SignalVM &vm)
|
||||||
|
{
|
||||||
|
if (this->opcode == PSO_FIRST) {
|
||||||
|
DEBUG(misc, 7, " Executing First");
|
||||||
|
vm.instruction = this->next;
|
||||||
|
} else {
|
||||||
|
DEBUG(misc, 7, " Executing Last");
|
||||||
|
vm.instruction = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*virtual*/ void SignalSpecial::SetNext(SignalInstruction *next_insn)
|
||||||
|
{
|
||||||
|
this->next = next_insn;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalIf::PseudoInstruction::PseudoInstruction(SignalProgram *prog, SignalOpcode op)
|
||||||
|
: SignalInstruction(prog, op)
|
||||||
|
{}
|
||||||
|
|
||||||
|
SignalIf::PseudoInstruction::PseudoInstruction(SignalProgram *prog, SignalIf *block, SignalOpcode op)
|
||||||
|
: SignalInstruction(prog, op)
|
||||||
|
{
|
||||||
|
this->block = block;
|
||||||
|
if (op == PSO_IF_ELSE) {
|
||||||
|
previous = block;
|
||||||
|
} else if (op == PSO_IF_ENDIF) {
|
||||||
|
previous = block->if_true;
|
||||||
|
} else NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ void SignalIf::PseudoInstruction::Remove()
|
||||||
|
{
|
||||||
|
if (opcode == PSO_IF_ELSE) {
|
||||||
|
this->block->if_true = NULL;
|
||||||
|
while(this->block->if_false) this->block->if_false->Remove();
|
||||||
|
} else if (opcode == PSO_IF_ENDIF) {
|
||||||
|
this->block->if_false = NULL;
|
||||||
|
} else NOT_REACHED();
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ void SignalIf::PseudoInstruction::Evaluate(SignalVM &vm)
|
||||||
|
{
|
||||||
|
DEBUG(misc, 7, " Executing If Pseudo Instruction %s", opcode == PSO_IF_ELSE ? "Else" : "Endif");
|
||||||
|
vm.instruction = this->block->after;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ void SignalIf::PseudoInstruction::SetNext(SignalInstruction *next_insn)
|
||||||
|
{
|
||||||
|
if (this->opcode == PSO_IF_ELSE) {
|
||||||
|
this->block->if_false = next_insn;
|
||||||
|
} else if (this->opcode == PSO_IF_ENDIF) {
|
||||||
|
this->block->after = next_insn;
|
||||||
|
} else NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalIf::SignalIf(SignalProgram *prog, bool raw)
|
||||||
|
: SignalInstruction(prog, PSO_IF)
|
||||||
|
{
|
||||||
|
if (!raw) {
|
||||||
|
this->condition = new SignalSimpleCondition(PSC_ALWAYS);
|
||||||
|
this->if_true = new PseudoInstruction(prog, this, PSO_IF_ELSE);
|
||||||
|
this->if_false = new PseudoInstruction(prog, this, PSO_IF_ENDIF);
|
||||||
|
this->after = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ void SignalIf::Remove()
|
||||||
|
{
|
||||||
|
delete this->condition;
|
||||||
|
while (this->if_true) this->if_true->Remove();
|
||||||
|
|
||||||
|
this->previous->SetNext(this->after);
|
||||||
|
this->after->SetPrevious(this->previous);
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ void SignalIf::Insert(SignalInstruction *before_insn)
|
||||||
|
{
|
||||||
|
this->previous = before_insn->Previous();
|
||||||
|
before_insn->Previous()->SetNext(this);
|
||||||
|
before_insn->SetPrevious(this->if_false);
|
||||||
|
this->after = before_insn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SignalIf::SetCondition(SignalCondition *cond)
|
||||||
|
{
|
||||||
|
assert(cond != this->condition);
|
||||||
|
delete this->condition;
|
||||||
|
this->condition = cond;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ void SignalIf::Evaluate(SignalVM &vm)
|
||||||
|
{
|
||||||
|
bool is_true = this->condition->Evaluate(vm);
|
||||||
|
DEBUG(misc, 7, " Executing If, taking %s branch", is_true ? "then" : "else");
|
||||||
|
if (is_true) {
|
||||||
|
vm.instruction = this->if_true;
|
||||||
|
} else {
|
||||||
|
vm.instruction = this->if_false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ void SignalIf::SetNext(SignalInstruction *next_insn)
|
||||||
|
{
|
||||||
|
this->if_true = next_insn;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SignalSet::SignalSet(SignalProgram *prog, SignalState state)
|
||||||
|
: SignalInstruction(prog, PSO_SET_SIGNAL)
|
||||||
|
{
|
||||||
|
this->to_state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ void SignalSet::Remove()
|
||||||
|
{
|
||||||
|
this->next->SetPrevious(this->previous);
|
||||||
|
this->previous->SetNext(this->next);
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ void SignalSet::Evaluate(SignalVM &vm)
|
||||||
|
{
|
||||||
|
DEBUG(misc, 7, " Executing SetSignal, making %s", this->to_state? "green" : "red");
|
||||||
|
vm.state = this->to_state;
|
||||||
|
vm.instruction = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*virtual*/ void SignalSet::SetNext(SignalInstruction *next_insn)
|
||||||
|
{
|
||||||
|
this->next = next_insn;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalProgram *GetExistingSignalProgram(SignalReference ref)
|
||||||
|
{
|
||||||
|
ProgramList::iterator i = _signal_programs.find(ref);
|
||||||
|
if (i != _signal_programs.end()) {
|
||||||
|
assert(i->first == ref);
|
||||||
|
return i->second;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SignalProgram *GetSignalProgram(SignalReference ref)
|
||||||
|
{
|
||||||
|
SignalProgram *pr = GetExistingSignalProgram(ref);
|
||||||
|
if (!pr) {
|
||||||
|
pr = new SignalProgram(ref.tile, ref.track);
|
||||||
|
_signal_programs[ref] = pr;
|
||||||
|
} else assert(pr->tile == ref.tile && pr->track == ref.track);
|
||||||
|
return pr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeSignalProgram(SignalReference ref)
|
||||||
|
{
|
||||||
|
DeleteWindowById(WC_SIGNAL_PROGRAM, (ref.tile << 3) | ref.track);
|
||||||
|
ProgramList::iterator i = _signal_programs.find(ref);
|
||||||
|
if (i != _signal_programs.end()) {
|
||||||
|
delete i->second;
|
||||||
|
_signal_programs.erase(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeSignalPrograms()
|
||||||
|
{
|
||||||
|
ProgramList::iterator i, e;
|
||||||
|
for (i = _signal_programs.begin(), e = _signal_programs.end(); i != e;) {
|
||||||
|
delete i->second;
|
||||||
|
// Must postincrement here to avoid iterator invalidation
|
||||||
|
_signal_programs.erase(i++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalState RunSignalProgram(SignalReference ref, uint num_exits, uint num_green)
|
||||||
|
{
|
||||||
|
SignalProgram *program = GetSignalProgram(ref);
|
||||||
|
SignalVM vm;
|
||||||
|
vm.program = program;
|
||||||
|
vm.num_exits = num_exits;
|
||||||
|
vm.num_green = num_green;
|
||||||
|
|
||||||
|
vm.instruction = program->first_instruction;
|
||||||
|
vm.state = SIGNAL_STATE_RED;
|
||||||
|
|
||||||
|
DEBUG(misc, 7, "%d exits, of which %d green", vm.num_exits, vm.num_green);
|
||||||
|
vm.Execute();
|
||||||
|
DEBUG(misc, 7, "Returning %s", vm.state == SIGNAL_STATE_GREEN ? "green" : "red");
|
||||||
|
return vm.state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveProgramDependencies(SignalReference by, SignalReference on)
|
||||||
|
{
|
||||||
|
SignalProgram *prog = GetSignalProgram(by);
|
||||||
|
for (SignalInstruction **b = prog->instructions.Begin(), **i = b, **e = prog->instructions.End();
|
||||||
|
i != e; i++) {
|
||||||
|
SignalInstruction *insn = *i;
|
||||||
|
if (insn->Opcode() == PSO_IF) {
|
||||||
|
SignalIf* ifi = static_cast<SignalIf*>(insn);
|
||||||
|
if (ifi->condition->ConditionCode() == PSC_SIGNAL_STATE) {
|
||||||
|
SignalStateCondition* c = static_cast<SignalStateCondition*>(ifi->condition);
|
||||||
|
if(c->sig_tile == by.tile && TrackdirToTrack(c->sig_track) == by.track)
|
||||||
|
c->Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddTrackToSignalBuffer(by.tile, by.track, GetTileOwner(by.tile));
|
||||||
|
UpdateSignalsInBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SignalProgram::DebugPrintProgram()
|
||||||
|
{
|
||||||
|
DEBUG(misc, 5, "Program %p listing", this);
|
||||||
|
for (SignalInstruction **b = this->instructions.Begin(), **i = b, **e = this->instructions.End();
|
||||||
|
i != e; i++)
|
||||||
|
{
|
||||||
|
SignalInstruction *insn = *i;
|
||||||
|
DEBUG(misc, 5, " %ld: Opcode %d, prev %d", long(i - b), int(insn->Opcode()),
|
||||||
|
int(insn->Previous() ? insn->Previous()->Id() : -1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Insert a signal instruction into the signal program.
|
||||||
|
*
|
||||||
|
* @param tile The Tile on which to perform the operation
|
||||||
|
* @param p1 Flags and information
|
||||||
|
* - Bits 0-2 Which track the signal sits on
|
||||||
|
* - Bits 3-18 ID of instruction to insert before
|
||||||
|
* - Bits 19-26 Which opcode to create
|
||||||
|
* - Bits 27-31 Reserved
|
||||||
|
* @param p2 Depends upon instruction
|
||||||
|
* - PSO_SET_SIGNAL:
|
||||||
|
* - Colour to set the signal to
|
||||||
|
* @param text unused
|
||||||
|
*/
|
||||||
|
CommandCost CmdInsertSignalInstruction(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
||||||
|
{
|
||||||
|
Track track = Extract<Track, 0, 3>(p1);
|
||||||
|
uint instruction_id = GB(p1, 3, 16);
|
||||||
|
SignalOpcode op = Extract<SignalOpcode, 19, 8>(p1);
|
||||||
|
|
||||||
|
if (!IsValidTrack(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
|
||||||
|
return CMD_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsTileOwner(tile, _current_company))
|
||||||
|
return_cmd_error(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
|
||||||
|
|
||||||
|
SignalProgram *prog = GetExistingSignalProgram(SignalReference(tile, track));
|
||||||
|
if (!prog)
|
||||||
|
return_cmd_error(STR_ERR_PROGSIG_NOT_THERE);
|
||||||
|
if (instruction_id > prog->instructions.Length())
|
||||||
|
return_cmd_error(STR_ERR_PROGSIG_INVALID_INSTRUCTION);
|
||||||
|
|
||||||
|
bool exec = (flags & DC_EXEC) != 0;
|
||||||
|
|
||||||
|
SignalInstruction *insert_before = prog->instructions[instruction_id];
|
||||||
|
switch (op) {
|
||||||
|
case PSO_IF: {
|
||||||
|
if (!exec) return CommandCost();
|
||||||
|
SignalIf *if_ins = new SignalIf(prog);
|
||||||
|
if_ins->Insert(insert_before);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PSO_SET_SIGNAL: {
|
||||||
|
SignalState ss = (SignalState) p2;
|
||||||
|
if (ss > SIGNAL_STATE_MAX) return_cmd_error(STR_ERR_PROGSIG_INVALID_OPCODE);
|
||||||
|
if (!exec) return CommandCost();
|
||||||
|
|
||||||
|
SignalSet *set = new SignalSet(prog, ss);
|
||||||
|
set->Insert(insert_before);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PSO_FIRST:
|
||||||
|
case PSO_LAST:
|
||||||
|
case PSO_IF_ELSE:
|
||||||
|
case PSO_IF_ENDIF:
|
||||||
|
default:
|
||||||
|
return_cmd_error(STR_ERR_PROGSIG_INVALID_OPCODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exec) return CommandCost();
|
||||||
|
AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
|
||||||
|
UpdateSignalsInBuffer();
|
||||||
|
InvalidateWindowData(WC_SIGNAL_PROGRAM, (tile << 3) | track);
|
||||||
|
return CommandCost();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Modify a singal instruction
|
||||||
|
*
|
||||||
|
* @param tile The Tile on which to perform the operation
|
||||||
|
* @param p1 Flags and information
|
||||||
|
* - Bits 0-2 Which track the signal sits on
|
||||||
|
* - Bits 3-18 ID of instruction to insert before
|
||||||
|
* @param p2 Depends upon instruction
|
||||||
|
* - PSO_SET_SIGNAL:
|
||||||
|
* - Colour to set the signal to
|
||||||
|
* - PSO_IF:
|
||||||
|
* - Bit 0 If 0, set the condidion code:
|
||||||
|
* - Bit 1-8: Conditon code to change to
|
||||||
|
* - Otherwise, if SignalVariableCondition:
|
||||||
|
* - Bits 1-2: Which field to change (ConditionField)
|
||||||
|
* - Bits 3-31: Value to set field to
|
||||||
|
* - Otherwise, if SignalStateCondition:
|
||||||
|
* - Bits 1-4: Trackdir on which signal is located
|
||||||
|
* - Bits 5-31: Tile on which signal is located
|
||||||
|
* @param text unused
|
||||||
|
*/
|
||||||
|
CommandCost CmdModifySignalInstruction(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
||||||
|
{
|
||||||
|
Track track = Extract<Track, 0, 3 >(p1);
|
||||||
|
uint instruction_id = GB(p1, 3, 16);
|
||||||
|
|
||||||
|
if (!IsValidTrack(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
|
||||||
|
return CMD_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsTileOwner(tile, _current_company))
|
||||||
|
return_cmd_error(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
|
||||||
|
|
||||||
|
SignalProgram *prog = GetExistingSignalProgram(SignalReference(tile, track));
|
||||||
|
if (!prog)
|
||||||
|
return_cmd_error(STR_ERR_PROGSIG_NOT_THERE);
|
||||||
|
|
||||||
|
if (instruction_id > prog->instructions.Length())
|
||||||
|
return_cmd_error(STR_ERR_PROGSIG_INVALID_INSTRUCTION);
|
||||||
|
|
||||||
|
bool exec = (flags & DC_EXEC) != 0;
|
||||||
|
|
||||||
|
SignalInstruction *insn = prog->instructions[instruction_id];
|
||||||
|
switch (insn->Opcode()) {
|
||||||
|
case PSO_SET_SIGNAL: {
|
||||||
|
SignalState state = (SignalState) p2;
|
||||||
|
if (state > SIGNAL_STATE_MAX)
|
||||||
|
return_cmd_error(STR_ERR_PROGSIG_INVALID_SIGNAL_STATE);
|
||||||
|
if (!exec)
|
||||||
|
return CommandCost();
|
||||||
|
SignalSet *ss = static_cast<SignalSet*>(insn);
|
||||||
|
ss->to_state = state;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case PSO_IF: {
|
||||||
|
SignalIf *si = static_cast<SignalIf*>(insn);
|
||||||
|
byte act = GB(p2, 0, 1);
|
||||||
|
if (act == 0) { // Set code
|
||||||
|
SignalConditionCode code = (SignalConditionCode) GB(p2, 1, 8);
|
||||||
|
if (code > PSC_MAX)
|
||||||
|
return_cmd_error(STR_ERR_PROGSIG_INVALID_CONDITION);
|
||||||
|
if (!exec) return CommandCost();
|
||||||
|
|
||||||
|
SignalCondition *cond;
|
||||||
|
switch (code) {
|
||||||
|
case PSC_ALWAYS:
|
||||||
|
case PSC_NEVER:
|
||||||
|
cond = new SignalSimpleCondition(code);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PSC_NUM_GREEN:
|
||||||
|
case PSC_NUM_RED:
|
||||||
|
cond = new SignalVariableCondition(code);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PSC_SIGNAL_STATE:
|
||||||
|
cond = new SignalStateCondition(SignalReference(tile, track), INVALID_TILE, INVALID_TRACKDIR);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: NOT_REACHED();
|
||||||
|
}
|
||||||
|
si->SetCondition(cond);
|
||||||
|
} else { // modify condition
|
||||||
|
switch (si->condition->ConditionCode()) {
|
||||||
|
case PSC_ALWAYS:
|
||||||
|
case PSC_NEVER:
|
||||||
|
return CommandCost(STR_ERR_PROGSIG_INVALID_CONDITION_FIELD);
|
||||||
|
|
||||||
|
case PSC_NUM_GREEN:
|
||||||
|
case PSC_NUM_RED: {
|
||||||
|
SignalVariableCondition *vc = static_cast<SignalVariableCondition*>(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();
|
||||||
|
vc->comparator = (SignalComparator) val;
|
||||||
|
} else if (f == SCF_VALUE) {
|
||||||
|
if (!exec) return CommandCost();
|
||||||
|
vc->value = val;
|
||||||
|
} else CommandCost(STR_ERR_PROGSIG_INVALID_CONDITION_FIELD);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case PSC_SIGNAL_STATE: {
|
||||||
|
SignalStateCondition *sc = static_cast<SignalStateCondition*>(si->condition);
|
||||||
|
Trackdir td = (Trackdir) GB(p2, 1, 4);
|
||||||
|
TileIndex ti = (TileIndex) GB(p2, 5, 27);
|
||||||
|
|
||||||
|
if (!IsValidTile(ti) || !IsValidTrackdir(td) || !HasSignalOnTrackdir(ti, td)
|
||||||
|
|| GetTileOwner(ti) != _current_company)
|
||||||
|
return_cmd_error(STR_ERR_PROGSIG_INVALID_SIGNAL);
|
||||||
|
if (!exec) return CommandCost();
|
||||||
|
sc->SetSignal(ti, td);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case PSO_FIRST:
|
||||||
|
case PSO_LAST:
|
||||||
|
case PSO_IF_ELSE:
|
||||||
|
case PSO_IF_ENDIF:
|
||||||
|
default:
|
||||||
|
return CommandCost(STR_ERR_PROGSIG_INVALID_OPCODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exec) return CommandCost();
|
||||||
|
|
||||||
|
AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
|
||||||
|
UpdateSignalsInBuffer();
|
||||||
|
InvalidateWindowData(WC_SIGNAL_PROGRAM, (tile << 3) | track);
|
||||||
|
return CommandCost();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Remove an instruction from a signal program
|
||||||
|
*
|
||||||
|
* @param tile The Tile on which to perform the operation
|
||||||
|
* @param p1 Flags and information
|
||||||
|
* - Bits 0-2 Which track the signal sits on
|
||||||
|
* - Bits 3-18 ID of instruction to insert before
|
||||||
|
* @param p2 unused
|
||||||
|
* @param text unused
|
||||||
|
*/
|
||||||
|
CommandCost CmdRemoveSignalInstruction(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
|
||||||
|
{
|
||||||
|
Track track = Extract<Track, 0, 3 >(p1);
|
||||||
|
uint instruction_id = GB(p1, 3, 16);
|
||||||
|
|
||||||
|
if (!IsValidTrack(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
|
||||||
|
return CMD_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsTileOwner(tile, _current_company))
|
||||||
|
return_cmd_error(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
|
||||||
|
|
||||||
|
SignalProgram *prog = GetExistingSignalProgram(SignalReference(tile, track));
|
||||||
|
if (!prog)
|
||||||
|
return_cmd_error(STR_ERR_PROGSIG_NOT_THERE);
|
||||||
|
|
||||||
|
if (instruction_id > prog->instructions.Length())
|
||||||
|
return_cmd_error(STR_ERR_PROGSIG_INVALID_INSTRUCTION);
|
||||||
|
|
||||||
|
bool exec = (flags & DC_EXEC) != 0;
|
||||||
|
|
||||||
|
SignalInstruction *insn = prog->instructions[instruction_id];
|
||||||
|
switch (insn->Opcode()) {
|
||||||
|
case PSO_SET_SIGNAL:
|
||||||
|
case PSO_IF:
|
||||||
|
if (!exec) return CommandCost();
|
||||||
|
insn->Remove();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PSO_FIRST:
|
||||||
|
case PSO_LAST:
|
||||||
|
case PSO_IF_ELSE:
|
||||||
|
case PSO_IF_ENDIF:
|
||||||
|
default:
|
||||||
|
return_cmd_error(STR_ERR_PROGSIG_INVALID_OPCODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exec) return CommandCost();
|
||||||
|
AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
|
||||||
|
UpdateSignalsInBuffer();
|
||||||
|
InvalidateWindowData(WC_SIGNAL_PROGRAM, (tile << 3) | track);
|
||||||
|
return CommandCost();
|
||||||
|
}
|
395
src/programmable_signals.h
Normal file
395
src/programmable_signals.h
Normal file
@@ -0,0 +1,395 @@
|
|||||||
|
/* $Id$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 Signals */
|
||||||
|
|
||||||
|
#ifndef PROGRAMMABLE_SIGNALS_H
|
||||||
|
#define PROGRAMMABLE_SIGNALS_H
|
||||||
|
#include "rail_map.h"
|
||||||
|
#include "core/smallvec_type.hpp"
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
/** @defgroup progsigs Programmable Signals */
|
||||||
|
///@{
|
||||||
|
|
||||||
|
/** The Programmable Signal virtual machine.
|
||||||
|
*
|
||||||
|
* This structure contains the state of the currently executing signal program.
|
||||||
|
*/
|
||||||
|
struct SignalVM;
|
||||||
|
|
||||||
|
class SignalInstruction;
|
||||||
|
class SignalSpecial;
|
||||||
|
typedef SmallVector<SignalInstruction*, 4> InstructionList;
|
||||||
|
|
||||||
|
/** The actual programmable 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 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 NULL, 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 program->instructions.FindIndex(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 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();
|
||||||
|
void Invalidate();
|
||||||
|
|
||||||
|
virtual bool Evaluate(SignalVM& vm);
|
||||||
|
virtual ~SignalStateCondition();
|
||||||
|
|
||||||
|
SignalReference this_sig;
|
||||||
|
TileIndex sig_tile;
|
||||||
|
Trackdir sig_track;
|
||||||
|
SignalState state;
|
||||||
|
};
|
||||||
|
|
||||||
|
// -- 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 NULL.
|
||||||
|
*/
|
||||||
|
SignalInstruction *next;
|
||||||
|
|
||||||
|
virtual void SetNext(SignalInstruction *next_insn);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** If signal instruction. This is perhaps the most important, as without it,
|
||||||
|
* programmable 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 by, SignalReference on);
|
||||||
|
///@}
|
||||||
|
|
||||||
|
#endif
|
904
src/programmable_signals_gui.cpp
Normal file
904
src/programmable_signals_gui.cpp
Normal file
@@ -0,0 +1,904 @@
|
|||||||
|
/* $Id$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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_gui.cpp GUI related to programming signals */
|
||||||
|
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "programmable_signals.h"
|
||||||
|
#include "command_func.h"
|
||||||
|
#include "window_func.h"
|
||||||
|
#include "strings_func.h"
|
||||||
|
#include "string_func.h"
|
||||||
|
#include "viewport_func.h"
|
||||||
|
#include "textbuf_gui.h"
|
||||||
|
#include "company_func.h"
|
||||||
|
#include "widgets/dropdown_func.h"
|
||||||
|
#include "gui.h"
|
||||||
|
#include "gfx_func.h"
|
||||||
|
#include "tilehighlight_func.h"
|
||||||
|
#include "rail_map.h"
|
||||||
|
#include "tile_cmd.h"
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
|
#include "table/sprites.h"
|
||||||
|
#include "table/strings.h"
|
||||||
|
|
||||||
|
enum ProgramWindowWidgets {
|
||||||
|
PROGRAM_WIDGET_CAPTION,
|
||||||
|
PROGRAM_WIDGET_INSTRUCTION_LIST,
|
||||||
|
PROGRAM_WIDGET_SCROLLBAR,
|
||||||
|
|
||||||
|
PROGRAM_WIDGET_SEL_TOP_LEFT,
|
||||||
|
PROGRAM_WIDGET_SEL_TOP_MIDDLE,
|
||||||
|
PROGRAM_WIDGET_SEL_TOP_RIGHT,
|
||||||
|
|
||||||
|
PROGRAM_WIDGET_SET_STATE,
|
||||||
|
PROGRAM_WIDGET_COND_VARIABLE,
|
||||||
|
PROGRAM_WIDGET_COND_COMPARATOR,
|
||||||
|
PROGRAM_WIDGET_COND_VALUE,
|
||||||
|
PROGRAM_WIDGET_COND_GOTO_SIGNAL,
|
||||||
|
PROGRAM_WIDGET_COND_SET_SIGNAL,
|
||||||
|
|
||||||
|
PROGRAM_WIDGET_GOTO_SIGNAL,
|
||||||
|
PROGRAM_WIDGET_INSERT,
|
||||||
|
PROGRAM_WIDGET_REMOVE,
|
||||||
|
|
||||||
|
PROGRAM_WIDGET_REMOVE_PROGRAM,
|
||||||
|
PROGRAM_WIDGET_COPY_PROGRAM,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PanelWidgets {
|
||||||
|
// Left
|
||||||
|
DPL_COND_VARIABLE = 0,
|
||||||
|
DPL_SET_STATE,
|
||||||
|
|
||||||
|
// Middle
|
||||||
|
DPM_COND_COMPARATOR = 0,
|
||||||
|
DPM_COND_GOTO_SIGNAL,
|
||||||
|
|
||||||
|
// Right
|
||||||
|
DPR_COND_VALUE = 0,
|
||||||
|
DPR_COND_SET_SIGNAL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const StringID _program_insert[] = {
|
||||||
|
STR_PROGSIG_INSERT_IF,
|
||||||
|
STR_PROGSIG_INSERT_SET_SIGNAL,
|
||||||
|
INVALID_STRING_ID
|
||||||
|
};
|
||||||
|
|
||||||
|
static SignalOpcode OpcodeForIndex(int index)
|
||||||
|
{
|
||||||
|
switch (index) {
|
||||||
|
case 0: return PSO_IF;
|
||||||
|
case 1: return PSO_SET_SIGNAL;
|
||||||
|
default: NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsConditionComparator(SignalCondition *cond)
|
||||||
|
{
|
||||||
|
switch (cond->ConditionCode()) {
|
||||||
|
case PSC_NUM_GREEN:
|
||||||
|
case PSC_NUM_RED:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const StringID _program_condvar[] = {
|
||||||
|
/* PSC_ALWAYS */ STR_PROGSIG_COND_ALWAYS,
|
||||||
|
/* PSC_NEVER */ STR_PROGSIG_COND_NEVER,
|
||||||
|
/* PSC_NUM_GREEN */ STR_PROGSIG_CONDVAR_NUM_GREEN,
|
||||||
|
/* PSC_NUM_RED */ STR_PROGSIG_CONDVAR_NUM_RED,
|
||||||
|
/* PSC_SIGNAL_STATE*/ STR_PROGSIG_COND_SIGNAL_STATE,
|
||||||
|
INVALID_STRING_ID
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: These should probably lose the ORDER
|
||||||
|
static const StringID _program_comparator[] = {
|
||||||
|
/* SGC_EQUALS */ STR_ORDER_CONDITIONAL_COMPARATOR_EQUALS,
|
||||||
|
/* SGC_NOT_EQUALS */ STR_ORDER_CONDITIONAL_COMPARATOR_NOT_EQUALS,
|
||||||
|
/* SGC_LESS_THAN */ STR_ORDER_CONDITIONAL_COMPARATOR_LESS_THAN,
|
||||||
|
/* SGC_LESS_THAN_EQUALS */ STR_ORDER_CONDITIONAL_COMPARATOR_LESS_EQUALS,
|
||||||
|
/* SGC_MORE_THAN */ STR_ORDER_CONDITIONAL_COMPARATOR_MORE_THAN,
|
||||||
|
/* SGC_MORE_THAN_EQUALS */ STR_ORDER_CONDITIONAL_COMPARATOR_MORE_EQUALS,
|
||||||
|
/* SGC_IS_TRUE */ STR_ORDER_CONDITIONAL_COMPARATOR_IS_TRUE,
|
||||||
|
/* SGC_IS_FALSE */ STR_ORDER_CONDITIONAL_COMPARATOR_IS_FALSE,
|
||||||
|
INVALID_STRING_ID
|
||||||
|
};
|
||||||
|
|
||||||
|
static const StringID _program_sigstate[] = {
|
||||||
|
STR_COLOUR_RED,
|
||||||
|
STR_COLOUR_GREEN,
|
||||||
|
INVALID_STRING_ID
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Get the string for a condition */
|
||||||
|
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<SignalVariableCondition*>(cond);
|
||||||
|
string = STR_PROGSIG_COND_COMPARE;
|
||||||
|
SetDParam(0, _program_condvar[cond->ConditionCode()]);
|
||||||
|
SetDParam(1, _program_comparator[cv->comparator]);
|
||||||
|
SetDParam(2, cv->value);
|
||||||
|
} else {
|
||||||
|
string = _program_condvar[cond->ConditionCode()];
|
||||||
|
if (cond->ConditionCode() == PSC_SIGNAL_STATE) {
|
||||||
|
string = STR_PROGSIG_CONDVAR_SIGNAL_STATE;
|
||||||
|
SetDParam(0, static_cast<SignalStateCondition*>(cond)->IsSignalValid()
|
||||||
|
? STR_PROGSIG_CONDVAR_SIGNAL_STATE_SPECIFIED : STR_PROGSIG_CONDVAR_SIGNAL_STATE_UNSPECIFIED);
|
||||||
|
SetDParam(1, selected ? STR_WHITE : STR_BLACK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return GetString(buf, string, buflast);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws an instruction in the programming GUI
|
||||||
|
* @param instruction The instruction to draw
|
||||||
|
* @param y Y position for drawing
|
||||||
|
* @param selected True, if the order is selected
|
||||||
|
* @param indent How many levels the instruction is indented
|
||||||
|
* @param left Left border for text drawing
|
||||||
|
* @param right Right border for text drawing
|
||||||
|
*/
|
||||||
|
static void DrawInstructionString(SignalInstruction *instruction, int y, bool selected, int indent, int left, int right)
|
||||||
|
{
|
||||||
|
StringID instruction_string = INVALID_STRING_ID;
|
||||||
|
|
||||||
|
char condstr[512];
|
||||||
|
|
||||||
|
switch (instruction->Opcode()) {
|
||||||
|
case PSO_FIRST:
|
||||||
|
instruction_string = STR_PROGSIG_FIRST;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PSO_LAST:
|
||||||
|
instruction_string = STR_PROGSIG_LAST;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PSO_IF: {
|
||||||
|
SignalIf *if_ins = static_cast<SignalIf*>(instruction);
|
||||||
|
GetConditionString(if_ins->condition, condstr, lastof(condstr), selected);
|
||||||
|
SetDParamStr(0, condstr);
|
||||||
|
instruction_string = STR_PROGSIG_IF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PSO_IF_ELSE:
|
||||||
|
instruction_string = STR_PROGSIG_ELSE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PSO_IF_ENDIF:
|
||||||
|
instruction_string = STR_PROGSIG_ENDIF;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PSO_SET_SIGNAL: {
|
||||||
|
instruction_string = STR_PROGSIG_SET_SIGNAL;
|
||||||
|
SignalSet *set = static_cast<SignalSet*>(instruction);
|
||||||
|
SetDParam(0, _program_sigstate[set->to_state]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawString(left + indent * 16, right, y, instruction_string, selected ? TC_WHITE : TC_BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GuiInstruction {
|
||||||
|
SignalInstruction *insn;
|
||||||
|
uint indent;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef SmallVector<GuiInstruction, 4> GuiInstructionList;
|
||||||
|
|
||||||
|
class ProgramWindow: public Window {
|
||||||
|
public:
|
||||||
|
ProgramWindow(WindowDesc *desc, SignalReference ref): Window(desc)
|
||||||
|
{
|
||||||
|
// this->InitNested(desc, (ref.tile << 3) | ref.track);
|
||||||
|
this->tile = ref.tile;
|
||||||
|
this->track = ref.track;
|
||||||
|
this->selected_instruction = -1;
|
||||||
|
|
||||||
|
this->CreateNestedTree(desc);
|
||||||
|
this->vscroll = this->GetScrollbar(PROGRAM_WIDGET_SCROLLBAR);
|
||||||
|
this->FinishInitNested((ref.tile << 3) | ref.track);
|
||||||
|
|
||||||
|
program = GetSignalProgram(ref);
|
||||||
|
RebuildInstructionList();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnClick(Point pt, int widget, int click_count) OVERRIDE
|
||||||
|
{
|
||||||
|
switch (widget) {
|
||||||
|
case PROGRAM_WIDGET_INSTRUCTION_LIST: {
|
||||||
|
int sel = this->GetInstructionFromPt(pt.y);
|
||||||
|
|
||||||
|
this->DeleteChildWindows();
|
||||||
|
HideDropDownMenu(this);
|
||||||
|
|
||||||
|
if (sel == -1 || this->GetOwner() != _local_company) {
|
||||||
|
// Deselect
|
||||||
|
this->selected_instruction = -1;
|
||||||
|
} else {
|
||||||
|
this->selected_instruction = sel;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->UpdateButtonState();
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case PROGRAM_WIDGET_INSERT: {
|
||||||
|
DEBUG(misc, 5, "Selection is %d", this->selected_instruction);
|
||||||
|
if (this->GetOwner() != _local_company || this->selected_instruction < 1)
|
||||||
|
return;
|
||||||
|
ShowDropDownMenu(this, _program_insert, -1, PROGRAM_WIDGET_INSERT, 0, 0, 0);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case PROGRAM_WIDGET_REMOVE: {
|
||||||
|
SignalInstruction *ins = GetSelected();
|
||||||
|
if (this->GetOwner() != _local_company || !ins)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32 p1 = 0;
|
||||||
|
SB(p1, 0, 3, this->track);
|
||||||
|
SB(p1, 3, 16, ins->Id());
|
||||||
|
|
||||||
|
DoCommandP(this->tile, p1, 0, CMD_REMOVE_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_MODIFY_INSTRUCTION));
|
||||||
|
this->RebuildInstructionList();
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case PROGRAM_WIDGET_SET_STATE: {
|
||||||
|
SignalInstruction *si = this->GetSelected();
|
||||||
|
if (!si || si->Opcode() != PSO_SET_SIGNAL) return;
|
||||||
|
SignalSet *ss = static_cast <SignalSet*>(si);
|
||||||
|
|
||||||
|
ShowDropDownMenu(this, _program_sigstate, ss->to_state, PROGRAM_WIDGET_SET_STATE, 0, 0, 0);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case PROGRAM_WIDGET_COND_VARIABLE: {
|
||||||
|
SignalInstruction *si = this->GetSelected();
|
||||||
|
if (!si || si->Opcode() != PSO_IF) return;
|
||||||
|
SignalIf *sif = static_cast <SignalIf*>(si);
|
||||||
|
|
||||||
|
ShowDropDownMenu(this, _program_condvar, sif->condition->ConditionCode(), PROGRAM_WIDGET_COND_VARIABLE, 0, 0, 0);
|
||||||
|
this->UpdateButtonState();
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case PROGRAM_WIDGET_COND_COMPARATOR: {
|
||||||
|
SignalInstruction *si = this->GetSelected();
|
||||||
|
if (!si || si->Opcode() != PSO_IF) return;
|
||||||
|
SignalIf *sif = static_cast <SignalIf*>(si);
|
||||||
|
if (!IsConditionComparator(sif->condition)) return;
|
||||||
|
SignalVariableCondition *vc = static_cast<SignalVariableCondition*>(sif->condition);
|
||||||
|
|
||||||
|
ShowDropDownMenu(this, _program_comparator, vc->comparator, PROGRAM_WIDGET_COND_COMPARATOR, 0, 0, 0);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case PROGRAM_WIDGET_COND_VALUE: {
|
||||||
|
SignalInstruction *si = this->GetSelected();
|
||||||
|
if (!si || si->Opcode() != PSO_IF) return;
|
||||||
|
SignalIf *sif = static_cast <SignalIf*>(si);
|
||||||
|
if (!IsConditionComparator(sif->condition)) return;
|
||||||
|
SignalVariableCondition *vc = static_cast<SignalVariableCondition*>(sif->condition);
|
||||||
|
|
||||||
|
SetDParam(0, vc->value);
|
||||||
|
//ShowQueryString(STR_JUST_INT, STR_PROGSIG_CONDITION_VALUE_CAPT, 5, 100, this, CS_NUMERAL, QSF_NONE);
|
||||||
|
ShowQueryString(STR_JUST_INT, STR_PROGSIG_CONDITION_VALUE_CAPT, 5, this, CS_NUMERAL, QSF_NONE);
|
||||||
|
this->UpdateButtonState();
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case PROGRAM_WIDGET_COND_GOTO_SIGNAL: {
|
||||||
|
SignalInstruction *si = this->GetSelected();
|
||||||
|
if (!si || si->Opcode() != PSO_IF) return;
|
||||||
|
SignalIf *sif = static_cast <SignalIf*>(si);
|
||||||
|
if (sif->condition->ConditionCode() != PSC_SIGNAL_STATE) return;
|
||||||
|
SignalStateCondition *sc = static_cast<SignalStateCondition*>(sif->condition);
|
||||||
|
|
||||||
|
if (sc->IsSignalValid()) {
|
||||||
|
ScrollMainWindowToTile(sc->sig_tile);
|
||||||
|
} else {
|
||||||
|
ShowErrorMessage(STR_ERROR_CAN_T_GOTO_UNDEFINED_SIGNAL, STR_EMPTY, WL_INFO);
|
||||||
|
}
|
||||||
|
// this->RaiseWidget(PROGRAM_WIDGET_COND_GOTO_SIGNAL);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case PROGRAM_WIDGET_COND_SET_SIGNAL: {
|
||||||
|
this->ToggleWidgetLoweredState(PROGRAM_WIDGET_COND_SET_SIGNAL);
|
||||||
|
this->SetWidgetDirty(PROGRAM_WIDGET_COND_SET_SIGNAL);
|
||||||
|
if (this->IsWidgetLowered(PROGRAM_WIDGET_COND_SET_SIGNAL)) {
|
||||||
|
SetObjectToPlaceWnd(ANIMCURSOR_BUILDSIGNALS, PAL_NONE, HT_RECT, this);
|
||||||
|
} else {
|
||||||
|
ResetObjectToPlace();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case PROGRAM_WIDGET_GOTO_SIGNAL: {
|
||||||
|
ScrollMainWindowToTile(this->tile);
|
||||||
|
// this->RaiseWidget(PROGRAM_WIDGET_GOTO_SIGNAL);
|
||||||
|
} break;
|
||||||
|
case PROGRAM_WIDGET_REMOVE_PROGRAM: {
|
||||||
|
if (this->GetOwner() != _local_company)
|
||||||
|
return;
|
||||||
|
program->first_instruction->Remove();
|
||||||
|
|
||||||
|
this->RebuildInstructionList();
|
||||||
|
} break;
|
||||||
|
case PROGRAM_WIDGET_COPY_PROGRAM: {
|
||||||
|
|
||||||
|
this->ToggleWidgetLoweredState(PROGRAM_WIDGET_COPY_PROGRAM);
|
||||||
|
this->SetWidgetDirty(PROGRAM_WIDGET_COPY_PROGRAM);
|
||||||
|
if (this->IsWidgetLowered(PROGRAM_WIDGET_COPY_PROGRAM)) {
|
||||||
|
SetObjectToPlaceWnd(ANIMCURSOR_BUILDSIGNALS, PAL_NONE, HT_RECT, this);
|
||||||
|
} else {
|
||||||
|
ResetObjectToPlace();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InsertInstruction(SignalInstruction *si, uint32 next)
|
||||||
|
{
|
||||||
|
uint64 p1 = 0;
|
||||||
|
while(true) {
|
||||||
|
switch(si->Opcode()) {
|
||||||
|
case PSO_SET_SIGNAL: {
|
||||||
|
SB(p1, 0, 3, this->track);
|
||||||
|
SB(p1, 3, 16, next);
|
||||||
|
SB(p1, 19, 8, si->Opcode());
|
||||||
|
|
||||||
|
DoCommandP(this->tile, p1, 0, CMD_INSERT_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_INSERT_INSTRUCTION));
|
||||||
|
this->RebuildInstructionList();
|
||||||
|
si = ((SignalSet*)si)->next;
|
||||||
|
} break;
|
||||||
|
case PSO_IF: {
|
||||||
|
SB(p1, 0, 3, this->track);
|
||||||
|
SB(p1, 3, 16, next);
|
||||||
|
SB(p1, 19, 8, si->Opcode());
|
||||||
|
|
||||||
|
DoCommandP(this->tile, p1, 0, CMD_INSERT_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_INSERT_INSTRUCTION));
|
||||||
|
this->RebuildInstructionList();
|
||||||
|
|
||||||
|
SignalInstruction *s = ((SignalIf*)si)->if_true;
|
||||||
|
while(s->Opcode() != PSO_IF_ELSE) {
|
||||||
|
if(s->Opcode() == PSO_IF) s = ((SignalIf*)s)->after;
|
||||||
|
if(s->Opcode() == PSO_SET_SIGNAL) s = ((SignalSet*)s)->next;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
InsertInstruction(((SignalIf*)si)->if_true, s->Id());
|
||||||
|
this->RebuildInstructionList();
|
||||||
|
|
||||||
|
s = ((SignalIf*)si)->if_false;
|
||||||
|
while(s->Opcode() != PSO_IF_ENDIF) {
|
||||||
|
if(s->Opcode() == PSO_IF) s = ((SignalIf*)s)->after;
|
||||||
|
if(s->Opcode() == PSO_SET_SIGNAL) s = ((SignalSet*)s)->next;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
InsertInstruction(((SignalIf*)si)->if_false, s->Id());
|
||||||
|
this->RebuildInstructionList();
|
||||||
|
|
||||||
|
si = ((SignalIf*)si)->after;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
if(si == NULL) break;
|
||||||
|
if(si->Opcode() == PSO_LAST) break;
|
||||||
|
if(si->Opcode() == PSO_IF_ELSE) break;
|
||||||
|
if(si->Opcode() == PSO_IF_ENDIF) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnPlaceObject(Point pt, TileIndex tile1) OVERRIDE
|
||||||
|
{
|
||||||
|
if (this->IsWidgetLowered(PROGRAM_WIDGET_COPY_PROGRAM)) {
|
||||||
|
//Copy program from another progsignal
|
||||||
|
TrackBits trackbits = TrackStatusToTrackBits(GetTileTrackStatus(tile1, TRANSPORT_RAIL, 0));
|
||||||
|
if (trackbits & TRACK_BIT_VERT) { // N-S direction
|
||||||
|
trackbits = (_tile_fract_coords.x <= _tile_fract_coords.y) ? TRACK_BIT_RIGHT : TRACK_BIT_LEFT;
|
||||||
|
}
|
||||||
|
if (trackbits & TRACK_BIT_HORZ) { // E-W direction
|
||||||
|
trackbits = (_tile_fract_coords.x + _tile_fract_coords.y <= 15) ? TRACK_BIT_UPPER : TRACK_BIT_LOWER;
|
||||||
|
}
|
||||||
|
Track track1 = FindFirstTrack(trackbits);
|
||||||
|
if(track1 == INVALID_TRACK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Trackdir td = TrackToTrackdir(track1);
|
||||||
|
Trackdir tdr = ReverseTrackdir(td);
|
||||||
|
if (!(HasSignalOnTrackdir(tile1, td) || HasSignalOnTrackdir(tile1, tdr)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (GetSignalType(tile1, track1) != SIGTYPE_PROG) {
|
||||||
|
ShowErrorMessage(STR_ERROR_INVALID_SIGNAL, STR_ERROR_NOT_AN_PROG_SIGNAL, WL_INFO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(this->tile == tile1 && this->track == track1) {
|
||||||
|
ShowErrorMessage(STR_ERROR_INVALID_SIGNAL, STR_ERROR_CANNOT_USE_SELF, WL_INFO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalProgram *sp = GetExistingSignalProgram(SignalReference(tile1, track1));
|
||||||
|
if (!sp) {
|
||||||
|
ShowErrorMessage(STR_ERROR_INVALID_SIGNAL, STR_ERROR_NOT_AN_EXIT_SIGNAL, WL_INFO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
program->first_instruction->Remove();
|
||||||
|
this->RebuildInstructionList();
|
||||||
|
|
||||||
|
SignalInstruction *si = ((SignalSpecial*)sp->first_instruction)->next;
|
||||||
|
InsertInstruction(si, program->last_instruction->Id());
|
||||||
|
ResetObjectToPlace();
|
||||||
|
this->RaiseWidget(PROGRAM_WIDGET_COPY_PROGRAM);
|
||||||
|
//OnPaint(); // this appears to cause visual artefacts
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalInstruction *si = this->GetSelected();
|
||||||
|
if (!si || si->Opcode() != PSO_IF) return;
|
||||||
|
SignalIf *sif = static_cast <SignalIf*>(si);
|
||||||
|
if (sif->condition->ConditionCode() != PSC_SIGNAL_STATE) return;
|
||||||
|
|
||||||
|
if (!IsPlainRailTile(tile1)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrackBits trackbits = TrackStatusToTrackBits(GetTileTrackStatus(tile1, TRANSPORT_RAIL, 0));
|
||||||
|
if (trackbits & TRACK_BIT_VERT) { // N-S direction
|
||||||
|
trackbits = (_tile_fract_coords.x <= _tile_fract_coords.y) ? TRACK_BIT_RIGHT : TRACK_BIT_LEFT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trackbits & TRACK_BIT_HORZ) { // E-W direction
|
||||||
|
trackbits = (_tile_fract_coords.x + _tile_fract_coords.y <= 15) ? TRACK_BIT_UPPER : TRACK_BIT_LOWER;
|
||||||
|
}
|
||||||
|
Track track1 = FindFirstTrack(trackbits);
|
||||||
|
if(track1 == INVALID_TRACK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Trackdir td = TrackToTrackdir(track1);
|
||||||
|
Trackdir tdr = ReverseTrackdir(td);
|
||||||
|
|
||||||
|
if (HasSignalOnTrackdir(tile1, td) && HasSignalOnTrackdir(tile1, tdr)) {
|
||||||
|
ShowErrorMessage(STR_ERROR_INVALID_SIGNAL, STR_ERROR_CAN_T_DEPEND_UPON_BIDIRECTIONAL_SIGNALS, WL_INFO);
|
||||||
|
return;
|
||||||
|
} else if (HasSignalOnTrackdir(tile1, tdr) && !HasSignalOnTrackdir(tile1, td)) {
|
||||||
|
td = tdr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!HasSignalOnTrackdir(tile1, td)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//!!!!!!!!!!!!!!!
|
||||||
|
if (!(GetSignalType(tile1, track1) == SIGTYPE_EXIT || GetSignalType(tile1, track1) == SIGTYPE_PROG)) {
|
||||||
|
//!!!!!!!!!!!!!!!
|
||||||
|
ShowErrorMessage(STR_ERROR_INVALID_SIGNAL, STR_ERROR_NOT_AN_EXIT_SIGNAL, WL_INFO);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 p1 = 0, p2 = 0;
|
||||||
|
SB(p1, 0, 3, this->track);
|
||||||
|
SB(p1, 3, 16, si->Id());
|
||||||
|
|
||||||
|
SB(p2, 0, 1, 1);
|
||||||
|
SB(p2, 1, 4, td);
|
||||||
|
SB(p2, 5, 27, tile1);
|
||||||
|
|
||||||
|
DoCommandP(this->tile, p1, p2, CMD_MODIFY_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_MODIFY_INSTRUCTION));
|
||||||
|
ResetObjectToPlace();
|
||||||
|
this->RaiseWidget(PROGRAM_WIDGET_COND_SET_SIGNAL);
|
||||||
|
//OnPaint(); // this appears to cause visual artefacts
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnQueryTextFinished(char *str) OVERRIDE
|
||||||
|
{
|
||||||
|
if (!StrEmpty(str)) {
|
||||||
|
SignalInstruction *si = this->GetSelected();
|
||||||
|
if (!si || si->Opcode() != PSO_IF) return;
|
||||||
|
SignalIf *sif = static_cast <SignalIf*>(si);
|
||||||
|
if (!IsConditionComparator(sif->condition)) return;
|
||||||
|
|
||||||
|
uint value = atoi(str);
|
||||||
|
|
||||||
|
uint32 p1 = 0, p2 = 0;
|
||||||
|
SB(p1, 0, 3, this->track);
|
||||||
|
SB(p1, 3, 16, si->Id());
|
||||||
|
|
||||||
|
SB(p2, 0, 1, 1);
|
||||||
|
SB(p2, 1, 2, SCF_VALUE);
|
||||||
|
SB(p2, 3, 27, value);
|
||||||
|
|
||||||
|
DoCommandP(this->tile, p1, p2, CMD_MODIFY_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_MODIFY_INSTRUCTION));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnDropdownSelect(int widget, int index) OVERRIDE
|
||||||
|
{
|
||||||
|
SignalInstruction *ins = this->GetSelected();
|
||||||
|
if (!ins) return;
|
||||||
|
|
||||||
|
switch (widget) {
|
||||||
|
case PROGRAM_WIDGET_INSERT: {
|
||||||
|
uint64 p1 = 0;
|
||||||
|
SB(p1, 0, 3, this->track);
|
||||||
|
SB(p1, 3, 16, ins->Id());
|
||||||
|
SB(p1, 19, 8, OpcodeForIndex(index));
|
||||||
|
|
||||||
|
DoCommandP(this->tile, p1, 0, CMD_INSERT_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_INSERT_INSTRUCTION));
|
||||||
|
this->RebuildInstructionList();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PROGRAM_WIDGET_SET_STATE: {
|
||||||
|
uint64 p1 = 0;
|
||||||
|
SB(p1, 0, 3, this->track);
|
||||||
|
SB(p1, 3, 16, ins->Id());
|
||||||
|
|
||||||
|
DoCommandP(this->tile, p1, index, CMD_MODIFY_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_MODIFY_INSTRUCTION));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PROGRAM_WIDGET_COND_VARIABLE: {
|
||||||
|
uint64 p1 = 0, p2 = 0;
|
||||||
|
SB(p1, 0, 3, this->track);
|
||||||
|
SB(p1, 3, 16, ins->Id());
|
||||||
|
|
||||||
|
SB(p2, 0, 1, 0);
|
||||||
|
SB(p2, 1, 8, index);
|
||||||
|
|
||||||
|
DoCommandP(this->tile, p1, p2, CMD_MODIFY_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_MODIFY_INSTRUCTION));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PROGRAM_WIDGET_COND_COMPARATOR: {
|
||||||
|
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_COMPARATOR);
|
||||||
|
SB(p2, 3, 27, index);
|
||||||
|
|
||||||
|
DoCommandP(this->tile, p1, p2, CMD_MODIFY_SIGNAL_INSTRUCTION | CMD_MSG(STR_ERROR_CAN_T_MODIFY_INSTRUCTION));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) OVERRIDE
|
||||||
|
{
|
||||||
|
switch (widget) {
|
||||||
|
case PROGRAM_WIDGET_INSTRUCTION_LIST:
|
||||||
|
resize->height = FONT_HEIGHT_NORMAL;
|
||||||
|
size->height = 6 * resize->height + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnResize() OVERRIDE
|
||||||
|
{
|
||||||
|
/* Update the scroll bar */
|
||||||
|
this->vscroll->SetCapacityFromWidget(this, PROGRAM_WIDGET_INSTRUCTION_LIST);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnPaint() OVERRIDE
|
||||||
|
{
|
||||||
|
this->DrawWidgets();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void DrawWidget(const Rect &r, int widget) const OVERRIDE
|
||||||
|
{
|
||||||
|
if (widget != PROGRAM_WIDGET_INSTRUCTION_LIST) return;
|
||||||
|
|
||||||
|
int y = r.top + WD_FRAMERECT_TOP;
|
||||||
|
int line_height = this->GetWidget<NWidgetBase>(PROGRAM_WIDGET_INSTRUCTION_LIST)->resize_y;
|
||||||
|
|
||||||
|
int no = this->vscroll->GetPosition();
|
||||||
|
|
||||||
|
for (const GuiInstruction *i = instructions.Begin() + no, *e = instructions.End();
|
||||||
|
i != e; ++i, no++) {
|
||||||
|
/* Don't draw anything if it extends past the end of the window. */
|
||||||
|
if (!this->vscroll->IsVisible(no)) break;
|
||||||
|
|
||||||
|
DrawInstructionString(i->insn, y, no == this->selected_instruction, i->indent, r.left + WD_FRAMETEXT_LEFT, r.right - WD_FRAMETEXT_RIGHT);
|
||||||
|
y += line_height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnInvalidateData(int data, bool gui_scope) OVERRIDE {
|
||||||
|
if (gui_scope) {
|
||||||
|
this->RebuildInstructionList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
virtual void SetStringParameters(int widget) const OVERRIDE
|
||||||
|
{
|
||||||
|
switch (widget) {
|
||||||
|
case PROGRAM_WIDGET_COND_VALUE: {
|
||||||
|
SetDParam(0, 0);
|
||||||
|
SignalInstruction *insn = this->GetSelected();
|
||||||
|
if (!insn || insn->Opcode() != PSO_IF) return;
|
||||||
|
SignalIf *si = static_cast<SignalIf*>(insn);
|
||||||
|
if (!IsConditionComparator(si->condition)) return;
|
||||||
|
SignalVariableCondition *vc = static_cast<SignalVariableCondition*>(si->condition);
|
||||||
|
SetDParam(0, vc->value);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SignalInstruction *GetSelected() const
|
||||||
|
{
|
||||||
|
if (this->selected_instruction == -1
|
||||||
|
|| this->selected_instruction >= int(this->instructions.Length()))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return this->instructions[this->selected_instruction].insn;
|
||||||
|
}
|
||||||
|
|
||||||
|
Owner GetOwner()
|
||||||
|
{
|
||||||
|
return GetTileOwner(this->tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetInstructionFromPt(int y)
|
||||||
|
{
|
||||||
|
NWidgetBase *nwid = this->GetWidget<NWidgetBase>(PROGRAM_WIDGET_INSTRUCTION_LIST);
|
||||||
|
int sel = (y - nwid->pos_y - WD_FRAMERECT_TOP) / nwid->resize_y; // Selected line
|
||||||
|
|
||||||
|
if ((uint)sel >= this->vscroll->GetCapacity()) return -1;
|
||||||
|
|
||||||
|
sel += this->vscroll->GetPosition();
|
||||||
|
|
||||||
|
return (sel <= int(this->instructions.Length()) && sel >= 0) ? sel : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RebuildInstructionList()
|
||||||
|
{
|
||||||
|
uint old_len = this->instructions.Length();
|
||||||
|
this->instructions.Clear();
|
||||||
|
SignalInstruction *insn = program->first_instruction;
|
||||||
|
uint indent = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
DEBUG(misc, 5, "PSig Gui: Opcode %d", insn->Opcode());
|
||||||
|
switch (insn->Opcode()) {
|
||||||
|
case PSO_FIRST:
|
||||||
|
case PSO_LAST: {
|
||||||
|
SignalSpecial *s = static_cast<SignalSpecial*>(insn);
|
||||||
|
GuiInstruction *gi = this->instructions.Append();
|
||||||
|
gi->insn = s;
|
||||||
|
gi->indent = indent;
|
||||||
|
insn = s->next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PSO_IF: {
|
||||||
|
SignalIf *i = static_cast<SignalIf*>(insn);
|
||||||
|
GuiInstruction *gi = this->instructions.Append();
|
||||||
|
gi->insn = i;
|
||||||
|
gi->indent = indent++;
|
||||||
|
insn = i->if_true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PSO_IF_ELSE: {
|
||||||
|
SignalIf::PseudoInstruction *p = static_cast<SignalIf::PseudoInstruction*>(insn);
|
||||||
|
GuiInstruction *gi = this->instructions.Append();
|
||||||
|
gi->insn = p;
|
||||||
|
gi->indent = indent - 1;
|
||||||
|
insn = p->block->if_false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PSO_IF_ENDIF: {
|
||||||
|
SignalIf::PseudoInstruction *p = static_cast<SignalIf::PseudoInstruction*>(insn);
|
||||||
|
GuiInstruction *gi = this->instructions.Append();
|
||||||
|
gi->insn = p;
|
||||||
|
gi->indent = --indent;
|
||||||
|
insn = p->block->after;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PSO_SET_SIGNAL: {
|
||||||
|
SignalSet *s = static_cast<SignalSet*>(insn);
|
||||||
|
GuiInstruction *gi = this->instructions.Append();
|
||||||
|
gi->insn = s;
|
||||||
|
gi->indent = indent;
|
||||||
|
insn = s->next;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: NOT_REACHED();
|
||||||
|
}
|
||||||
|
} while (insn);
|
||||||
|
|
||||||
|
this->vscroll->SetCount(this->instructions.Length());
|
||||||
|
if (this->instructions.Length() != old_len)
|
||||||
|
selected_instruction = -1;
|
||||||
|
UpdateButtonState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateButtonState()
|
||||||
|
{
|
||||||
|
// Do not close the Signals GUI when opening the ProgrammableSignals GUI
|
||||||
|
// ResetObjectToPlace();
|
||||||
|
this->RaiseWidget(PROGRAM_WIDGET_INSERT);
|
||||||
|
this->RaiseWidget(PROGRAM_WIDGET_REMOVE);
|
||||||
|
this->RaiseWidget(PROGRAM_WIDGET_SET_STATE);
|
||||||
|
this->RaiseWidget(PROGRAM_WIDGET_COND_VARIABLE);
|
||||||
|
this->RaiseWidget(PROGRAM_WIDGET_COND_COMPARATOR);
|
||||||
|
this->RaiseWidget(PROGRAM_WIDGET_COND_VALUE);
|
||||||
|
this->RaiseWidget(PROGRAM_WIDGET_COND_GOTO_SIGNAL);
|
||||||
|
|
||||||
|
NWidgetStacked *left_sel = this->GetWidget<NWidgetStacked>(PROGRAM_WIDGET_SEL_TOP_LEFT);
|
||||||
|
NWidgetStacked *middle_sel = this->GetWidget<NWidgetStacked>(PROGRAM_WIDGET_SEL_TOP_MIDDLE);
|
||||||
|
NWidgetStacked *right_sel = this->GetWidget<NWidgetStacked>(PROGRAM_WIDGET_SEL_TOP_RIGHT);
|
||||||
|
|
||||||
|
// Disable all the modifier buttons - we will re-enable them if applicable
|
||||||
|
this->DisableWidget(PROGRAM_WIDGET_SET_STATE);
|
||||||
|
this->DisableWidget(PROGRAM_WIDGET_COND_VARIABLE);
|
||||||
|
this->DisableWidget(PROGRAM_WIDGET_COND_COMPARATOR);
|
||||||
|
this->DisableWidget(PROGRAM_WIDGET_COND_VALUE);
|
||||||
|
this->DisableWidget(PROGRAM_WIDGET_COND_SET_SIGNAL);
|
||||||
|
this->DisableWidget(PROGRAM_WIDGET_COND_GOTO_SIGNAL);
|
||||||
|
|
||||||
|
// Don't allow modifications if don't own, or have selected invalid instruction
|
||||||
|
if (this->GetOwner() != _local_company || this->selected_instruction < 1) {
|
||||||
|
this->DisableWidget(PROGRAM_WIDGET_INSERT);
|
||||||
|
this->DisableWidget(PROGRAM_WIDGET_REMOVE);
|
||||||
|
this->SetDirty();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this->EnableWidget(PROGRAM_WIDGET_INSERT);
|
||||||
|
this->EnableWidget(PROGRAM_WIDGET_REMOVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalInstruction *insn = GetSelected();
|
||||||
|
if (!insn) return;
|
||||||
|
|
||||||
|
switch (insn->Opcode()) {
|
||||||
|
case PSO_IF: {
|
||||||
|
SignalIf *i = static_cast<SignalIf*>(insn);
|
||||||
|
left_sel->SetDisplayedPlane(DPL_COND_VARIABLE);
|
||||||
|
middle_sel->SetDisplayedPlane(DPM_COND_COMPARATOR);
|
||||||
|
right_sel->SetDisplayedPlane(DPR_COND_VALUE);
|
||||||
|
|
||||||
|
this->EnableWidget(PROGRAM_WIDGET_COND_VARIABLE);
|
||||||
|
this->GetWidget<NWidgetCore>(PROGRAM_WIDGET_COND_VARIABLE)->widget_data =
|
||||||
|
_program_condvar[i->condition->ConditionCode()];
|
||||||
|
|
||||||
|
if (IsConditionComparator(i->condition)) {
|
||||||
|
SignalVariableCondition *vc = static_cast<SignalVariableCondition*>(i->condition);
|
||||||
|
this->EnableWidget(PROGRAM_WIDGET_COND_COMPARATOR);
|
||||||
|
this->EnableWidget(PROGRAM_WIDGET_COND_VALUE);
|
||||||
|
|
||||||
|
this->GetWidget<NWidgetCore>(PROGRAM_WIDGET_COND_COMPARATOR)->widget_data =
|
||||||
|
_program_comparator[vc->comparator];
|
||||||
|
|
||||||
|
} else if (i->condition->ConditionCode() == PSC_SIGNAL_STATE) {
|
||||||
|
this->EnableWidget(PROGRAM_WIDGET_COND_GOTO_SIGNAL);
|
||||||
|
this->EnableWidget(PROGRAM_WIDGET_COND_SET_SIGNAL);
|
||||||
|
middle_sel->SetDisplayedPlane(DPM_COND_GOTO_SIGNAL);
|
||||||
|
right_sel->SetDisplayedPlane(DPR_COND_SET_SIGNAL);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case PSO_SET_SIGNAL: {
|
||||||
|
SignalSet *s = static_cast<SignalSet*>(insn);
|
||||||
|
left_sel->SetDisplayedPlane(DPL_SET_STATE);
|
||||||
|
this->SetWidgetDisabledState(PROGRAM_WIDGET_SET_STATE, false);
|
||||||
|
this->GetWidget<NWidgetCore>(PROGRAM_WIDGET_SET_STATE)->widget_data =
|
||||||
|
_program_sigstate[s->to_state];
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case PSO_FIRST:
|
||||||
|
case PSO_LAST:
|
||||||
|
case PSO_IF_ELSE:
|
||||||
|
case PSO_IF_ENDIF:
|
||||||
|
// All cannot be modified
|
||||||
|
this->DisableWidget(PROGRAM_WIDGET_REMOVE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
NOT_REACHED();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->SetDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
TileIndex tile;
|
||||||
|
Track track;
|
||||||
|
SignalProgram *program;
|
||||||
|
GuiInstructionList instructions;
|
||||||
|
int selected_instruction;
|
||||||
|
Scrollbar *vscroll;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const NWidgetPart _nested_program_widgets[] = {
|
||||||
|
// Title bar
|
||||||
|
NWidget(NWID_HORIZONTAL),
|
||||||
|
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
|
||||||
|
NWidget(WWT_CAPTION, COLOUR_GREY, PROGRAM_WIDGET_CAPTION), SetDataTip(STR_PROGSIG_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
|
||||||
|
NWidget(WWT_SHADEBOX, COLOUR_GREY),
|
||||||
|
NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
|
||||||
|
NWidget(WWT_STICKYBOX, COLOUR_GREY),
|
||||||
|
EndContainer(),
|
||||||
|
|
||||||
|
// 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(NWID_VSCROLLBAR, COLOUR_GREY, PROGRAM_WIDGET_SCROLLBAR),
|
||||||
|
EndContainer(),
|
||||||
|
|
||||||
|
// Button Bar
|
||||||
|
NWidget(NWID_HORIZONTAL),
|
||||||
|
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||||
|
NWidget(NWID_SELECTION, INVALID_COLOUR, PROGRAM_WIDGET_SEL_TOP_LEFT),
|
||||||
|
NWidget(WWT_DROPDOWN, COLOUR_GREY, PROGRAM_WIDGET_COND_VARIABLE), SetMinimalSize(124, 12), SetFill(1, 0),
|
||||||
|
SetDataTip(STR_NULL, STR_PROGSIG_COND_VARIABLE_TOOLTIP), SetResize(1, 0),
|
||||||
|
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_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),
|
||||||
|
NWidget(WWT_TEXTBTN, COLOUR_GREY, PROGRAM_WIDGET_COND_GOTO_SIGNAL), SetMinimalSize(124, 12), SetFill(1, 0),
|
||||||
|
SetDataTip(STR_PROGSIG_GOTO_SIGNAL, STR_PROGSIG_GOTO_SIGNAL_TOOLTIP), SetResize(1, 0),
|
||||||
|
EndContainer(),
|
||||||
|
NWidget(NWID_SELECTION, INVALID_COLOUR, PROGRAM_WIDGET_SEL_TOP_RIGHT),
|
||||||
|
NWidget(WWT_TEXTBTN, COLOUR_GREY, PROGRAM_WIDGET_COND_VALUE), SetMinimalSize(124, 12), SetFill(1, 0),
|
||||||
|
SetDataTip(STR_BLACK_COMMA, STR_PROGSIG_COND_VALUE_TOOLTIP), SetResize(1, 0),
|
||||||
|
NWidget(WWT_TEXTBTN, COLOUR_GREY, PROGRAM_WIDGET_COND_SET_SIGNAL), SetMinimalSize(124, 12), SetFill(1, 0),
|
||||||
|
SetDataTip(STR_PROGSIG_COND_SET_SIGNAL, STR_PROGSIG_COND_SET_SIGNAL_TOOLTIP), SetResize(1, 0),
|
||||||
|
EndContainer(),
|
||||||
|
EndContainer(),
|
||||||
|
NWidget(WWT_IMGBTN, COLOUR_GREY, PROGRAM_WIDGET_GOTO_SIGNAL), SetMinimalSize(12, 12), SetDataTip(SPR_ARROW_RIGHT, STR_PROGSIG_GOTO_SIGNAL_TOOLTIP),
|
||||||
|
EndContainer(),
|
||||||
|
|
||||||
|
/* Second button row. */
|
||||||
|
NWidget(NWID_HORIZONTAL),
|
||||||
|
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||||
|
NWidget(WWT_DROPDOWN, COLOUR_GREY, PROGRAM_WIDGET_INSERT), SetMinimalSize(124, 12), SetFill(1, 0),
|
||||||
|
SetDataTip(STR_PROGSIG_INSERT, STR_PROGSIG_INSERT_TOOLTIP), SetResize(1, 0),
|
||||||
|
NWidget(WWT_TEXTBTN, COLOUR_GREY, PROGRAM_WIDGET_REMOVE), SetMinimalSize(186, 12), SetFill(1, 0),
|
||||||
|
SetDataTip(STR_PROGSIG_REMOVE, STR_PROGSIG_REMOVE_TOOLTIP), SetResize(1, 0),
|
||||||
|
EndContainer(),
|
||||||
|
EndContainer(),
|
||||||
|
|
||||||
|
/* Third button row*/
|
||||||
|
NWidget(NWID_HORIZONTAL),
|
||||||
|
NWidget(WWT_TEXTBTN, COLOUR_GREY, PROGRAM_WIDGET_REMOVE_PROGRAM), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_PROGSIG_REMOVE_PROGRAM, STR_PROGSIG_REMOVE_PROGRAM_TOOLTIP), SetResize(1, 0),
|
||||||
|
NWidget(WWT_TEXTBTN, COLOUR_GREY, PROGRAM_WIDGET_COPY_PROGRAM), SetMinimalSize(124, 12), SetFill(1, 0), SetDataTip(STR_PROGSIG_COPY_PROGRAM, STR_PROGSIG_COPY_PROGRAM_TOOLTIP), SetResize(1, 0),
|
||||||
|
NWidget(WWT_RESIZEBOX, COLOUR_GREY),
|
||||||
|
EndContainer(),
|
||||||
|
};
|
||||||
|
|
||||||
|
static WindowDesc _program_desc(
|
||||||
|
WDP_AUTO, "signal_program", 384, 100,
|
||||||
|
WC_SIGNAL_PROGRAM, WC_BUILD_SIGNAL,
|
||||||
|
WDF_CONSTRUCTION,
|
||||||
|
_nested_program_widgets, lengthof(_nested_program_widgets)
|
||||||
|
);
|
||||||
|
|
||||||
|
void ShowSignalProgramWindow(SignalReference ref)
|
||||||
|
{
|
||||||
|
uint32 window_id = (ref.tile << 3) | ref.track;
|
||||||
|
if (BringWindowToFrontById(WC_SIGNAL_PROGRAM, window_id) != NULL) return;
|
||||||
|
|
||||||
|
new ProgramWindow(&_program_desc, ref);
|
||||||
|
}
|
@@ -33,6 +33,7 @@
|
|||||||
#include "strings_func.h"
|
#include "strings_func.h"
|
||||||
#include "company_gui.h"
|
#include "company_gui.h"
|
||||||
#include "object_map.h"
|
#include "object_map.h"
|
||||||
|
#include "programmable_signals.h"
|
||||||
|
|
||||||
#include "table/strings.h"
|
#include "table/strings.h"
|
||||||
#include "table/railtypes.h"
|
#include "table/railtypes.h"
|
||||||
@@ -93,10 +94,12 @@ void ResolveRailTypeGUISprites(RailtypeInfo *rti)
|
|||||||
/* Array of default GUI signal sprite numbers. */
|
/* Array of default GUI signal sprite numbers. */
|
||||||
const SpriteID _signal_lookup[2][SIGTYPE_END] = {
|
const SpriteID _signal_lookup[2][SIGTYPE_END] = {
|
||||||
{SPR_IMG_SIGNAL_ELECTRIC_NORM, SPR_IMG_SIGNAL_ELECTRIC_ENTRY, SPR_IMG_SIGNAL_ELECTRIC_EXIT,
|
{SPR_IMG_SIGNAL_ELECTRIC_NORM, SPR_IMG_SIGNAL_ELECTRIC_ENTRY, SPR_IMG_SIGNAL_ELECTRIC_EXIT,
|
||||||
SPR_IMG_SIGNAL_ELECTRIC_COMBO, SPR_IMG_SIGNAL_ELECTRIC_PBS, SPR_IMG_SIGNAL_ELECTRIC_PBS_OWAY},
|
SPR_IMG_SIGNAL_ELECTRIC_COMBO, SPR_IMG_SIGNAL_ELECTRIC_PBS, SPR_IMG_SIGNAL_ELECTRIC_PBS_OWAY,
|
||||||
|
SPR_IMG_SIGNAL_ELECTRIC_PROG},
|
||||||
|
|
||||||
{SPR_IMG_SIGNAL_SEMAPHORE_NORM, SPR_IMG_SIGNAL_SEMAPHORE_ENTRY, SPR_IMG_SIGNAL_SEMAPHORE_EXIT,
|
{SPR_IMG_SIGNAL_SEMAPHORE_NORM, SPR_IMG_SIGNAL_SEMAPHORE_ENTRY, SPR_IMG_SIGNAL_SEMAPHORE_EXIT,
|
||||||
SPR_IMG_SIGNAL_SEMAPHORE_COMBO, SPR_IMG_SIGNAL_SEMAPHORE_PBS, SPR_IMG_SIGNAL_SEMAPHORE_PBS_OWAY},
|
SPR_IMG_SIGNAL_SEMAPHORE_COMBO, SPR_IMG_SIGNAL_SEMAPHORE_PBS, SPR_IMG_SIGNAL_SEMAPHORE_PBS_OWAY,
|
||||||
|
SPR_IMG_SIGNAL_SEMAPHORE_PROG},
|
||||||
};
|
};
|
||||||
|
|
||||||
for (SignalType type = SIGTYPE_NORMAL; type < SIGTYPE_END; type = (SignalType)(type + 1)) {
|
for (SignalType type = SIGTYPE_NORMAL; type < SIGTYPE_END; type = (SignalType)(type + 1)) {
|
||||||
@@ -642,6 +645,7 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1,
|
|||||||
|
|
||||||
/* Charge extra to remove signals on the track, if they are there */
|
/* Charge extra to remove signals on the track, if they are there */
|
||||||
if (HasSignalOnTrack(tile, track)) {
|
if (HasSignalOnTrack(tile, track)) {
|
||||||
|
CheckRemoveSignal(tile, track);
|
||||||
cost.AddCost(DoCommand(tile, track, 0, flags, CMD_REMOVE_SIGNALS));
|
cost.AddCost(DoCommand(tile, track, 0, flags, CMD_REMOVE_SIGNALS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -991,8 +995,7 @@ CommandCost CmdBuildTrainDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, u
|
|||||||
* - p1 = (bit 4) - 0 = signals, 1 = semaphores
|
* - p1 = (bit 4) - 0 = signals, 1 = semaphores
|
||||||
* - p1 = (bit 5-7) - type of the signal, for valid values see enum SignalType in rail_map.h
|
* - p1 = (bit 5-7) - type of the signal, for valid values see enum SignalType in rail_map.h
|
||||||
* - p1 = (bit 8) - convert the present signal type and variant
|
* - p1 = (bit 8) - convert the present signal type and variant
|
||||||
* - p1 = (bit 9-11)- start cycle from this signal type
|
* - p1 = (bit 9-14)- cycle through which signal set?
|
||||||
* - p1 = (bit 12-14)-wrap around after this signal type
|
|
||||||
* - p1 = (bit 15-16)-cycle the signal direction this many times
|
* - p1 = (bit 15-16)-cycle the signal direction this many times
|
||||||
* - p1 = (bit 17) - 1 = don't modify an existing signal but don't fail either, 0 = always set new signal type
|
* - p1 = (bit 17) - 1 = don't modify an existing signal but don't fail either, 0 = always set new signal type
|
||||||
* @param p2 used for CmdBuildManySignals() to copy direction of first signal
|
* @param p2 used for CmdBuildManySignals() to copy direction of first signal
|
||||||
@@ -1007,12 +1010,9 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
|
|||||||
SignalVariant sigvar = (ctrl_pressed ^ HasBit(p1, 4)) ? SIG_SEMAPHORE : SIG_ELECTRIC; // the signal variant of the new signal
|
SignalVariant sigvar = (ctrl_pressed ^ HasBit(p1, 4)) ? SIG_SEMAPHORE : SIG_ELECTRIC; // the signal variant of the new signal
|
||||||
SignalType sigtype = Extract<SignalType, 5, 3>(p1); // the signal type of the new signal
|
SignalType sigtype = Extract<SignalType, 5, 3>(p1); // the signal type of the new signal
|
||||||
bool convert_signal = HasBit(p1, 8); // convert button pressed
|
bool convert_signal = HasBit(p1, 8); // convert button pressed
|
||||||
SignalType cycle_start = Extract<SignalType, 9, 3>(p1);
|
|
||||||
SignalType cycle_stop = Extract<SignalType, 12, 3>(p1);
|
|
||||||
uint num_dir_cycle = GB(p1, 15, 2);
|
uint num_dir_cycle = GB(p1, 15, 2);
|
||||||
|
|
||||||
if (sigtype > SIGTYPE_LAST) return CMD_ERROR;
|
uint which_signals = GB(p1, 9, 6);
|
||||||
if (cycle_start > cycle_stop || cycle_stop > SIGTYPE_LAST) return CMD_ERROR;
|
|
||||||
|
|
||||||
/* You can only build signals on plain rail tiles, and the selected track must exist */
|
/* You can only build signals on plain rail tiles, and the selected track must exist */
|
||||||
if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) ||
|
if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) ||
|
||||||
@@ -1098,6 +1098,8 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
|
|||||||
sigtype = GetSignalType(tile, track);
|
sigtype = GetSignalType(tile, track);
|
||||||
} else {
|
} else {
|
||||||
/* convert the present signal to the chosen type and variant */
|
/* convert the present signal to the chosen type and variant */
|
||||||
|
if (IsPresignalProgrammable(tile, track))
|
||||||
|
FreeSignalProgram(SignalReference(tile, track));
|
||||||
SetSignalType(tile, track, sigtype);
|
SetSignalType(tile, track, sigtype);
|
||||||
SetSignalVariant(tile, track, sigvar);
|
SetSignalVariant(tile, track, sigvar);
|
||||||
if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
|
if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
|
||||||
@@ -1106,10 +1108,12 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if (ctrl_pressed) {
|
} else if (ctrl_pressed) {
|
||||||
/* cycle between cycle_start and cycle_end */
|
/* cycle through signal types */
|
||||||
sigtype = (SignalType)(GetSignalType(tile, track) + 1);
|
sigtype = (SignalType)(GetSignalType(tile, track));
|
||||||
|
if(IsProgrammableSignal(sigtype))
|
||||||
|
FreeSignalProgram(SignalReference(tile, track));
|
||||||
|
|
||||||
if (sigtype < cycle_start || sigtype > cycle_stop) sigtype = cycle_start;
|
sigtype = NextSignalType(sigtype, which_signals);
|
||||||
|
|
||||||
SetSignalType(tile, track, sigtype);
|
SetSignalType(tile, track, sigtype);
|
||||||
if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
|
if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
|
||||||
@@ -1127,6 +1131,8 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1,
|
|||||||
* direction of the first signal given as parameter by CmdBuildManySignals */
|
* direction of the first signal given as parameter by CmdBuildManySignals */
|
||||||
SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | (p2 & SignalOnTrack(track)));
|
SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | (p2 & SignalOnTrack(track)));
|
||||||
SetSignalVariant(tile, track, sigvar);
|
SetSignalVariant(tile, track, sigvar);
|
||||||
|
if (IsPresignalProgrammable(tile, track))
|
||||||
|
FreeSignalProgram(SignalReference(tile, track));
|
||||||
SetSignalType(tile, track, sigtype);
|
SetSignalType(tile, track, sigtype);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1259,7 +1265,7 @@ static CommandCost CmdSignalTrackHelper(TileIndex tile, DoCommandFlag flags, uin
|
|||||||
/* Must start on a valid track to be able to avoid loops */
|
/* Must start on a valid track to be able to avoid loops */
|
||||||
if (!HasTrack(tile, track)) return CMD_ERROR;
|
if (!HasTrack(tile, track)) return CMD_ERROR;
|
||||||
|
|
||||||
SignalType sigtype = (SignalType)GB(p2, 7, 3);
|
SignalType sigtype = Extract<SignalType, 7, 3>(p2);
|
||||||
if (sigtype > SIGTYPE_LAST) return CMD_ERROR;
|
if (sigtype > SIGTYPE_LAST) return CMD_ERROR;
|
||||||
|
|
||||||
byte signals;
|
byte signals;
|
||||||
@@ -1449,6 +1455,7 @@ CommandCost CmdRemoveSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
|
Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
|
||||||
|
CheckRemoveSignal(tile, track);
|
||||||
SetPresentSignals(tile, GetPresentSignals(tile) & ~SignalOnTrack(track));
|
SetPresentSignals(tile, GetPresentSignals(tile) & ~SignalOnTrack(track));
|
||||||
Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
|
Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
|
||||||
DirtyCompanyInfrastructureWindows(GetTileOwner(tile));
|
DirtyCompanyInfrastructureWindows(GetTileOwner(tile));
|
||||||
@@ -1780,6 +1787,9 @@ static CommandCost ClearTile_Track(TileIndex tile, DoCommandFlag flags)
|
|||||||
|
|
||||||
switch (GetRailTileType(tile)) {
|
switch (GetRailTileType(tile)) {
|
||||||
case RAIL_TILE_SIGNALS:
|
case RAIL_TILE_SIGNALS:
|
||||||
|
if (flags & DC_EXEC) CheckRemoveSignalsFromTile(tile);
|
||||||
|
// FALL THROUGH
|
||||||
|
|
||||||
case RAIL_TILE_NORMAL: {
|
case RAIL_TILE_NORMAL: {
|
||||||
Slope tileh = GetTileSlope(tile);
|
Slope tileh = GetTileSlope(tile);
|
||||||
/* Is there flat water on the lower halftile that gets cleared expensively? */
|
/* Is there flat water on the lower halftile that gets cleared expensively? */
|
||||||
@@ -1865,9 +1875,14 @@ static void DrawSingleSignal(TileIndex tile, const RailtypeInfo *rti, Track trac
|
|||||||
} else {
|
} else {
|
||||||
/* Normal electric signals are stored in a different sprite block than all other signals. */
|
/* Normal electric signals are stored in a different sprite block than all other signals. */
|
||||||
sprite = (type == SIGTYPE_NORMAL && variant == SIG_ELECTRIC) ? SPR_ORIGINAL_SIGNALS_BASE : SPR_SIGNALS_BASE - 16;
|
sprite = (type == SIGTYPE_NORMAL && variant == SIG_ELECTRIC) ? SPR_ORIGINAL_SIGNALS_BASE : SPR_SIGNALS_BASE - 16;
|
||||||
sprite += type * 16 + variant * 64 + image * 2 + condition + (type > SIGTYPE_LAST_NOPBS ? 64 : 0);
|
sprite += type * 16 + variant * 64 + image * 2 + condition + (IsSignalSpritePBS(type) ? 64 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == SIGTYPE_PROG && variant == SIG_SEMAPHORE) {
|
||||||
|
sprite = SPR_PROGSIGNAL_BASE + image * 2 + condition;
|
||||||
|
} else if (type == SIGTYPE_PROG && variant == SIG_ELECTRIC) {
|
||||||
|
sprite = SPR_PROGSIGNAL_BASE + 16 + image * 2 + condition;
|
||||||
|
}
|
||||||
AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, GetSaveSlopeZ(x, y, track));
|
AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, GetSaveSlopeZ(x, y, track));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2707,14 +2722,15 @@ static void GetTileDesc_Track(TileIndex tile, TileDesc *td)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case RAIL_TILE_SIGNALS: {
|
case RAIL_TILE_SIGNALS: {
|
||||||
static const StringID signal_type[6][6] = {
|
static const StringID signal_type[7][7] = {
|
||||||
{
|
{
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_SIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_SIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS,
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PROGSIGNALS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
|
||||||
@@ -2722,7 +2738,8 @@ static void GetTileDesc_Track(TileIndex tile, TileDesc *td)
|
|||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS,
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PROGSIGNALS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
|
||||||
@@ -2730,7 +2747,8 @@ static void GetTileDesc_Track(TileIndex tile, TileDesc *td)
|
|||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXITSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXITSIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS,
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PROGSIGNALS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
|
||||||
@@ -2738,7 +2756,8 @@ static void GetTileDesc_Track(TileIndex tile, TileDesc *td)
|
|||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBOSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBOSIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS,
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PROGSIGNALS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
|
||||||
@@ -2746,7 +2765,8 @@ static void GetTileDesc_Track(TileIndex tile, TileDesc *td)
|
|||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBSSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBSSIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS,
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_PROGSIGNALS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS,
|
||||||
@@ -2754,7 +2774,17 @@ static void GetTileDesc_Track(TileIndex tile, TileDesc *td)
|
|||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS,
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS,
|
||||||
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NOENTRYSIGNALS
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NOENTRYSIGNALS,
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NOENTRY_PROGSIGNALS
|
||||||
|
},
|
||||||
|
{
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PROGSIGNALS,
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PROGSIGNALS,
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PROGSIGNALS,
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PROGSIGNALS,
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_PROGSIGNALS,
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NOENTRY_PROGSIGNALS,
|
||||||
|
STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PROGSIGNALS
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -34,6 +34,7 @@
|
|||||||
#include "vehicle_func.h"
|
#include "vehicle_func.h"
|
||||||
#include "zoom_func.h"
|
#include "zoom_func.h"
|
||||||
#include "rail_gui.h"
|
#include "rail_gui.h"
|
||||||
|
#include "programmable_signals.h"
|
||||||
|
|
||||||
#include "station_map.h"
|
#include "station_map.h"
|
||||||
#include "tunnelbridge_map.h"
|
#include "tunnelbridge_map.h"
|
||||||
@@ -49,8 +50,10 @@ static DiagDirection _build_depot_direction; ///< Currently selected depot direc
|
|||||||
static byte _waypoint_count = 1; ///< Number of waypoint types
|
static byte _waypoint_count = 1; ///< Number of waypoint types
|
||||||
static byte _cur_waypoint_type; ///< Currently selected waypoint type
|
static byte _cur_waypoint_type; ///< Currently selected waypoint type
|
||||||
static bool _convert_signal_button; ///< convert signal button in the signal GUI pressed
|
static bool _convert_signal_button; ///< convert signal button in the signal GUI pressed
|
||||||
|
static bool _program_signal_button; ///< program signal button in the signal GUI pressed
|
||||||
static SignalVariant _cur_signal_variant; ///< set the signal variant (for signal GUI)
|
static SignalVariant _cur_signal_variant; ///< set the signal variant (for signal GUI)
|
||||||
static SignalType _cur_signal_type; ///< set the signal type (for signal GUI)
|
static SignalType _cur_signal_type; ///< set the signal type (for signal GUI)
|
||||||
|
static uint _cur_signal_button; ///< set the signal button (for signal GUI)
|
||||||
|
|
||||||
/* Map the setting: default_signal_type to the corresponding signal type */
|
/* Map the setting: default_signal_type to the corresponding signal type */
|
||||||
static const SignalType _default_signal_type[] = {SIGTYPE_NORMAL, SIGTYPE_PBS, SIGTYPE_PBS_ONEWAY};
|
static const SignalType _default_signal_type[] = {SIGTYPE_NORMAL, SIGTYPE_PBS, SIGTYPE_PBS_ONEWAY};
|
||||||
@@ -224,11 +227,22 @@ static void GenericPlaceSignals(TileIndex tile)
|
|||||||
|
|
||||||
if (_remove_button_clicked) {
|
if (_remove_button_clicked) {
|
||||||
DoCommandP(tile, track, 0, CMD_REMOVE_SIGNALS | CMD_MSG(STR_ERROR_CAN_T_REMOVE_SIGNALS_FROM), CcPlaySound1E);
|
DoCommandP(tile, track, 0, CMD_REMOVE_SIGNALS | CMD_MSG(STR_ERROR_CAN_T_REMOVE_SIGNALS_FROM), CcPlaySound1E);
|
||||||
} else {
|
return;
|
||||||
const Window *w = FindWindowById(WC_BUILD_SIGNAL, 0);
|
}
|
||||||
|
|
||||||
/* Map the setting cycle_signal_types to the lower and upper allowed signal type. */
|
if (_program_signal_button) {
|
||||||
static const uint cycle_bounds[] = {SIGTYPE_NORMAL | (SIGTYPE_LAST_NOPBS << 3), SIGTYPE_PBS | (SIGTYPE_LAST << 3), SIGTYPE_NORMAL | (SIGTYPE_LAST << 3)};
|
if (IsPlainRailTile(tile) && HasTrack(tile, track) && HasSignalOnTrack(tile,track) && IsPresignalProgrammable(tile, track)) {
|
||||||
|
// Show program gui if there is a programmable signal
|
||||||
|
ShowSignalProgramWindow(SignalReference(tile, track));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't display error here even though program-button is pressed and there is no programmable signal,
|
||||||
|
// instead just handle it normally. That way player can keep the program-button pressed all the time
|
||||||
|
// to build slightly faster.
|
||||||
|
}
|
||||||
|
|
||||||
|
const Window *w = FindWindowById(WC_BUILD_SIGNAL, 0);
|
||||||
|
|
||||||
/* various bitstuffed elements for CmdBuildSingleSignal() */
|
/* various bitstuffed elements for CmdBuildSingleSignal() */
|
||||||
uint32 p1 = track;
|
uint32 p1 = track;
|
||||||
@@ -239,20 +253,19 @@ static void GenericPlaceSignals(TileIndex tile)
|
|||||||
SB(p1, 4, 1, _cur_signal_variant);
|
SB(p1, 4, 1, _cur_signal_variant);
|
||||||
SB(p1, 5, 3, _cur_signal_type);
|
SB(p1, 5, 3, _cur_signal_type);
|
||||||
SB(p1, 8, 1, _convert_signal_button);
|
SB(p1, 8, 1, _convert_signal_button);
|
||||||
SB(p1, 9, 6, cycle_bounds[_settings_client.gui.cycle_signal_types]);
|
SB(p1, 9, 6, _settings_client.gui.cycle_signal_types);
|
||||||
} else {
|
} else {
|
||||||
SB(p1, 3, 1, _ctrl_pressed);
|
SB(p1, 3, 1, _ctrl_pressed);
|
||||||
SB(p1, 4, 1, (_cur_year < _settings_client.gui.semaphore_build_before ? SIG_SEMAPHORE : SIG_ELECTRIC));
|
SB(p1, 4, 1, (_cur_year < _settings_client.gui.semaphore_build_before ? SIG_SEMAPHORE : SIG_ELECTRIC));
|
||||||
SB(p1, 5, 3, _default_signal_type[_settings_client.gui.default_signal_type]);
|
SB(p1, 5, 3, _default_signal_type[_settings_client.gui.default_signal_type]);
|
||||||
SB(p1, 8, 1, 0);
|
SB(p1, 8, 1, 0);
|
||||||
SB(p1, 9, 6, cycle_bounds[_settings_client.gui.cycle_signal_types]);
|
SB(p1, 9, 6, _settings_client.gui.cycle_signal_types);
|
||||||
}
|
}
|
||||||
|
|
||||||
DoCommandP(tile, p1, 0, CMD_BUILD_SIGNALS |
|
DoCommandP(tile, p1, 0, CMD_BUILD_SIGNALS |
|
||||||
CMD_MSG((w != NULL && _convert_signal_button) ? STR_ERROR_SIGNAL_CAN_T_CONVERT_SIGNALS_HERE : STR_ERROR_CAN_T_BUILD_SIGNALS_HERE),
|
CMD_MSG((w != NULL && _convert_signal_button) ? STR_ERROR_SIGNAL_CAN_T_CONVERT_SIGNALS_HERE : STR_ERROR_CAN_T_BUILD_SIGNALS_HERE),
|
||||||
CcPlaySound1E);
|
CcPlaySound1E);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start placing a rail bridge.
|
* Start placing a rail bridge.
|
||||||
@@ -1564,7 +1577,7 @@ public:
|
|||||||
{
|
{
|
||||||
if (IsInsideMM(widget, WID_BS_SEMAPHORE_NORM, WID_BS_ELECTRIC_PBS_OWAY + 1)) {
|
if (IsInsideMM(widget, WID_BS_SEMAPHORE_NORM, WID_BS_ELECTRIC_PBS_OWAY + 1)) {
|
||||||
/* Extract signal from widget number. */
|
/* Extract signal from widget number. */
|
||||||
int type = (widget - WID_BS_SEMAPHORE_NORM) % SIGTYPE_END;
|
SignalType type = TypeForClick((widget - WID_BS_SEMAPHORE_NORM) % SIGTYPE_END);
|
||||||
int var = SIG_SEMAPHORE - (widget - WID_BS_SEMAPHORE_NORM) / SIGTYPE_END; // SignalVariant order is reversed compared to the widgets.
|
int var = SIG_SEMAPHORE - (widget - WID_BS_SEMAPHORE_NORM) / SIGTYPE_END; // SignalVariant order is reversed compared to the widgets.
|
||||||
SpriteID sprite = GetRailTypeInfo(_cur_railtype)->gui_sprites.signals[type][var][this->IsWidgetLowered(widget)];
|
SpriteID sprite = GetRailTypeInfo(_cur_railtype)->gui_sprites.signals[type][var][this->IsWidgetLowered(widget)];
|
||||||
|
|
||||||
@@ -1572,6 +1585,22 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline SignalType TypeForClick(uint id) const
|
||||||
|
{
|
||||||
|
switch(id) {
|
||||||
|
case 0: return SIGTYPE_NORMAL;
|
||||||
|
case 1: return SIGTYPE_ENTRY;
|
||||||
|
case 2: return SIGTYPE_EXIT;
|
||||||
|
case 3: return SIGTYPE_COMBO;
|
||||||
|
case 4: return SIGTYPE_PROG;
|
||||||
|
case 5: return SIGTYPE_PBS;
|
||||||
|
case 6: return SIGTYPE_PBS_ONEWAY;
|
||||||
|
default:
|
||||||
|
assert(!"Bad signal type button ID");
|
||||||
|
return SIGTYPE_NORMAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
virtual void OnClick(Point pt, int widget, int click_count)
|
virtual void OnClick(Point pt, int widget, int click_count)
|
||||||
{
|
{
|
||||||
switch (widget) {
|
switch (widget) {
|
||||||
@@ -1579,17 +1608,20 @@ public:
|
|||||||
case WID_BS_SEMAPHORE_ENTRY:
|
case WID_BS_SEMAPHORE_ENTRY:
|
||||||
case WID_BS_SEMAPHORE_EXIT:
|
case WID_BS_SEMAPHORE_EXIT:
|
||||||
case WID_BS_SEMAPHORE_COMBO:
|
case WID_BS_SEMAPHORE_COMBO:
|
||||||
|
case WID_BS_SEMAPHORE_PROG:
|
||||||
case WID_BS_SEMAPHORE_PBS:
|
case WID_BS_SEMAPHORE_PBS:
|
||||||
case WID_BS_SEMAPHORE_PBS_OWAY:
|
case WID_BS_SEMAPHORE_PBS_OWAY:
|
||||||
case WID_BS_ELECTRIC_NORM:
|
case WID_BS_ELECTRIC_NORM:
|
||||||
case WID_BS_ELECTRIC_ENTRY:
|
case WID_BS_ELECTRIC_ENTRY:
|
||||||
case WID_BS_ELECTRIC_EXIT:
|
case WID_BS_ELECTRIC_EXIT:
|
||||||
case WID_BS_ELECTRIC_COMBO:
|
case WID_BS_ELECTRIC_COMBO:
|
||||||
|
case WID_BS_ELECTRIC_PROG:
|
||||||
case WID_BS_ELECTRIC_PBS:
|
case WID_BS_ELECTRIC_PBS:
|
||||||
case WID_BS_ELECTRIC_PBS_OWAY:
|
case WID_BS_ELECTRIC_PBS_OWAY:
|
||||||
this->RaiseWidget((_cur_signal_variant == SIG_ELECTRIC ? WID_BS_ELECTRIC_NORM : WID_BS_SEMAPHORE_NORM) + _cur_signal_type);
|
this->RaiseWidget((_cur_signal_variant == SIG_ELECTRIC ? WID_BS_ELECTRIC_NORM : WID_BS_SEMAPHORE_NORM) + _cur_signal_button);
|
||||||
|
|
||||||
_cur_signal_type = (SignalType)((uint)((widget - WID_BS_SEMAPHORE_NORM) % (SIGTYPE_LAST + 1)));
|
_cur_signal_button = (uint)((widget - WID_BS_SEMAPHORE_NORM) % (SIGTYPE_END));
|
||||||
|
_cur_signal_type = TypeForClick(_cur_signal_button);
|
||||||
_cur_signal_variant = widget >= WID_BS_ELECTRIC_NORM ? SIG_ELECTRIC : SIG_SEMAPHORE;
|
_cur_signal_variant = widget >= WID_BS_ELECTRIC_NORM ? SIG_ELECTRIC : SIG_SEMAPHORE;
|
||||||
|
|
||||||
/* If 'remove' button of rail build toolbar is active, disable it. */
|
/* If 'remove' button of rail build toolbar is active, disable it. */
|
||||||
@@ -1597,11 +1629,18 @@ public:
|
|||||||
Window *w = FindWindowById(WC_BUILD_TOOLBAR, TRANSPORT_RAIL);
|
Window *w = FindWindowById(WC_BUILD_TOOLBAR, TRANSPORT_RAIL);
|
||||||
if (w != NULL) ToggleRailButton_Remove(w);
|
if (w != NULL) ToggleRailButton_Remove(w);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WID_BS_CONVERT:
|
case WID_BS_CONVERT:
|
||||||
_convert_signal_button = !_convert_signal_button;
|
_convert_signal_button = !_convert_signal_button;
|
||||||
|
if(_convert_signal_button)
|
||||||
|
_program_signal_button = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WID_BS_PROGRAM:
|
||||||
|
_program_signal_button = !_program_signal_button;
|
||||||
|
if(_program_signal_button)
|
||||||
|
_convert_signal_button = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WID_BS_DRAG_SIGNALS_DENSITY_DECREASE:
|
case WID_BS_DRAG_SIGNALS_DENSITY_DECREASE:
|
||||||
@@ -1632,9 +1671,10 @@ public:
|
|||||||
virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
|
virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
|
||||||
{
|
{
|
||||||
if (!gui_scope) return;
|
if (!gui_scope) return;
|
||||||
this->LowerWidget((_cur_signal_variant == SIG_ELECTRIC ? WID_BS_ELECTRIC_NORM : WID_BS_SEMAPHORE_NORM) + _cur_signal_type);
|
this->LowerWidget((_cur_signal_variant == SIG_ELECTRIC ? WID_BS_ELECTRIC_NORM : WID_BS_SEMAPHORE_NORM) + _cur_signal_button);
|
||||||
|
|
||||||
this->SetWidgetLoweredState(WID_BS_CONVERT, _convert_signal_button);
|
this->SetWidgetLoweredState(WID_BS_CONVERT, _convert_signal_button);
|
||||||
|
this->SetWidgetLoweredState(WID_BS_PROGRAM, _program_signal_button);
|
||||||
|
|
||||||
this->SetWidgetDisabledState(WID_BS_DRAG_SIGNALS_DENSITY_DECREASE, _settings_client.gui.drag_signals_density == 1);
|
this->SetWidgetDisabledState(WID_BS_DRAG_SIGNALS_DENSITY_DECREASE, _settings_client.gui.drag_signals_density == 1);
|
||||||
this->SetWidgetDisabledState(WID_BS_DRAG_SIGNALS_DENSITY_INCREASE, _settings_client.gui.drag_signals_density == 20);
|
this->SetWidgetDisabledState(WID_BS_DRAG_SIGNALS_DENSITY_INCREASE, _settings_client.gui.drag_signals_density == 20);
|
||||||
@@ -1653,15 +1693,18 @@ static const NWidgetPart _nested_signal_builder_widgets[] = {
|
|||||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_ENTRY), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_ENTRY_TOOLTIP), EndContainer(), SetFill(1, 1),
|
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_ENTRY), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_ENTRY_TOOLTIP), EndContainer(), SetFill(1, 1),
|
||||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_EXIT), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_EXIT_TOOLTIP), EndContainer(), SetFill(1, 1),
|
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_EXIT), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_EXIT_TOOLTIP), EndContainer(), SetFill(1, 1),
|
||||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_COMBO), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_COMBO_TOOLTIP), EndContainer(), SetFill(1, 1),
|
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_COMBO), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_COMBO_TOOLTIP), EndContainer(), SetFill(1, 1),
|
||||||
|
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_PROG), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_PROG_TOOLTIP), EndContainer(), SetFill(1, 1),
|
||||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_PBS), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_PBS_TOOLTIP), EndContainer(), SetFill(1, 1),
|
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_PBS), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_PBS_TOOLTIP), EndContainer(), SetFill(1, 1),
|
||||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_PBS_OWAY), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_PBS_OWAY_TOOLTIP), EndContainer(), SetFill(1, 1),
|
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_SEMAPHORE_PBS_OWAY), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_SEMAPHORE_PBS_OWAY_TOOLTIP), EndContainer(), SetFill(1, 1),
|
||||||
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_BS_CONVERT), SetDataTip(SPR_IMG_SIGNAL_CONVERT, STR_BUILD_SIGNAL_CONVERT_TOOLTIP), SetFill(1, 1),
|
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_BS_CONVERT), SetDataTip(SPR_IMG_SIGNAL_CONVERT, STR_BUILD_SIGNAL_CONVERT_TOOLTIP), SetFill(1, 1),
|
||||||
|
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), EndContainer(), SetFill(1, 1),
|
||||||
EndContainer(),
|
EndContainer(),
|
||||||
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
|
||||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_NORM), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_NORM_TOOLTIP), EndContainer(), SetFill(1, 1),
|
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_NORM), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_NORM_TOOLTIP), EndContainer(), SetFill(1, 1),
|
||||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_ENTRY), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_ENTRY_TOOLTIP), EndContainer(), SetFill(1, 1),
|
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_ENTRY), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_ENTRY_TOOLTIP), EndContainer(), SetFill(1, 1),
|
||||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_EXIT), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_EXIT_TOOLTIP), EndContainer(), SetFill(1, 1),
|
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_EXIT), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_EXIT_TOOLTIP), EndContainer(), SetFill(1, 1),
|
||||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_COMBO), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_COMBO_TOOLTIP), EndContainer(), SetFill(1, 1),
|
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_COMBO), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_COMBO_TOOLTIP), EndContainer(), SetFill(1, 1),
|
||||||
|
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_PROG), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_PROG_TOOLTIP), EndContainer(), SetFill(1, 1),
|
||||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_PBS), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_PBS_TOOLTIP), EndContainer(), SetFill(1, 1),
|
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_PBS), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_PBS_TOOLTIP), EndContainer(), SetFill(1, 1),
|
||||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_PBS_OWAY), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_PBS_OWAY_TOOLTIP), EndContainer(), SetFill(1, 1),
|
NWidget(WWT_PANEL, COLOUR_DARK_GREEN, WID_BS_ELECTRIC_PBS_OWAY), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_ELECTRIC_PBS_OWAY_TOOLTIP), EndContainer(), SetFill(1, 1),
|
||||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_TOOLTIP), SetFill(1, 1),
|
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetDataTip(STR_NULL, STR_BUILD_SIGNAL_DRAG_SIGNALS_DENSITY_TOOLTIP), SetFill(1, 1),
|
||||||
@@ -1674,6 +1717,7 @@ static const NWidgetPart _nested_signal_builder_widgets[] = {
|
|||||||
EndContainer(),
|
EndContainer(),
|
||||||
NWidget(NWID_SPACER), SetMinimalSize(0, 2), SetFill(1, 0),
|
NWidget(NWID_SPACER), SetMinimalSize(0, 2), SetFill(1, 0),
|
||||||
EndContainer(),
|
EndContainer(),
|
||||||
|
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_BS_PROGRAM), SetDataTip(SPR_IMG_SETTINGS, STR_PROGRAM_SIGNAL_TOOLTIP), SetFill(1, 1),
|
||||||
EndContainer(),
|
EndContainer(),
|
||||||
EndContainer(),
|
EndContainer(),
|
||||||
};
|
};
|
||||||
@@ -1957,7 +2001,7 @@ bool ResetSignalVariant(int32 p)
|
|||||||
Window *w = FindWindowById(WC_BUILD_SIGNAL, 0);
|
Window *w = FindWindowById(WC_BUILD_SIGNAL, 0);
|
||||||
if (w != NULL) {
|
if (w != NULL) {
|
||||||
w->SetDirty();
|
w->SetDirty();
|
||||||
w->RaiseWidget((_cur_signal_variant == SIG_ELECTRIC ? WID_BS_ELECTRIC_NORM : WID_BS_SEMAPHORE_NORM) + _cur_signal_type);
|
w->RaiseWidget((_cur_signal_variant == SIG_ELECTRIC ? WID_BS_ELECTRIC_NORM : WID_BS_SEMAPHORE_NORM) + _cur_signal_button);
|
||||||
}
|
}
|
||||||
_cur_signal_variant = new_variant;
|
_cur_signal_variant = new_variant;
|
||||||
}
|
}
|
||||||
@@ -1974,7 +2018,12 @@ void InitializeRailGUI()
|
|||||||
SetDefaultRailGui();
|
SetDefaultRailGui();
|
||||||
|
|
||||||
_convert_signal_button = false;
|
_convert_signal_button = false;
|
||||||
|
_program_signal_button = false;
|
||||||
_cur_signal_type = _default_signal_type[_settings_client.gui.default_signal_type];
|
_cur_signal_type = _default_signal_type[_settings_client.gui.default_signal_type];
|
||||||
|
_cur_signal_button =
|
||||||
|
_cur_signal_type == SIGTYPE_PROG ? 4 :
|
||||||
|
_cur_signal_type == SIGTYPE_PBS ? 5 :
|
||||||
|
_cur_signal_type == SIGTYPE_PBS_ONEWAY ? 6 : _cur_signal_type;
|
||||||
ResetSignalVariant();
|
ResetSignalVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -285,12 +285,6 @@ static inline TrackBits GetDepotReservationTrackBits(TileIndex t)
|
|||||||
return HasDepotReservation(t) ? TrackToTrackBits(GetRailDepotTrack(t)) : TRACK_BIT_NONE;
|
return HasDepotReservation(t) ? TrackToTrackBits(GetRailDepotTrack(t)) : TRACK_BIT_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline bool IsPbsSignal(SignalType s)
|
|
||||||
{
|
|
||||||
return s == SIGTYPE_PBS || s == SIGTYPE_PBS_ONEWAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline SignalType GetSignalType(TileIndex t, Track track)
|
static inline SignalType GetSignalType(TileIndex t, Track track)
|
||||||
{
|
{
|
||||||
assert(GetRailTileType(t) == RAIL_TILE_SIGNALS);
|
assert(GetRailTileType(t) == RAIL_TILE_SIGNALS);
|
||||||
@@ -308,12 +302,22 @@ static inline void SetSignalType(TileIndex t, Track track, SignalType s)
|
|||||||
|
|
||||||
static inline bool IsPresignalEntry(TileIndex t, Track track)
|
static inline bool IsPresignalEntry(TileIndex t, Track track)
|
||||||
{
|
{
|
||||||
return GetSignalType(t, track) == SIGTYPE_ENTRY || GetSignalType(t, track) == SIGTYPE_COMBO;
|
return IsEntrySignal(GetSignalType(t, track));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool IsPresignalExit(TileIndex t, Track track)
|
static inline bool IsPresignalExit(TileIndex t, Track track)
|
||||||
{
|
{
|
||||||
return GetSignalType(t, track) == SIGTYPE_EXIT || GetSignalType(t, track) == SIGTYPE_COMBO;
|
return IsExitSignal(GetSignalType(t, track));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool IsPresignalCombo(TileIndex t, Track track)
|
||||||
|
{
|
||||||
|
return IsComboSignal(GetSignalType(t, track));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool IsPresignalProgrammable(TileIndex t, Track track)
|
||||||
|
{
|
||||||
|
return IsProgrammableSignal(GetSignalType(t, track));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** One-way signals can't be passed the 'wrong' way. */
|
/** One-way signals can't be passed the 'wrong' way. */
|
||||||
|
@@ -262,8 +262,9 @@
|
|||||||
* 192 26700
|
* 192 26700
|
||||||
* 193 26802
|
* 193 26802
|
||||||
* 194 26881 1.5.x
|
* 194 26881 1.5.x
|
||||||
|
* 200 Programmable Signals patch added
|
||||||
*/
|
*/
|
||||||
extern const uint16 SAVEGAME_VERSION = 194; ///< Current savegame version of OpenTTD.
|
extern const uint16 SAVEGAME_VERSION = 200; ///< Current savegame version of OpenTTD.
|
||||||
|
|
||||||
SavegameType _savegame_type; ///< type of savegame we are loading
|
SavegameType _savegame_type; ///< type of savegame we are loading
|
||||||
|
|
||||||
@@ -447,6 +448,7 @@ extern const ChunkHandler _linkgraph_chunk_handlers[];
|
|||||||
extern const ChunkHandler _airport_chunk_handlers[];
|
extern const ChunkHandler _airport_chunk_handlers[];
|
||||||
extern const ChunkHandler _object_chunk_handlers[];
|
extern const ChunkHandler _object_chunk_handlers[];
|
||||||
extern const ChunkHandler _persistent_storage_chunk_handlers[];
|
extern const ChunkHandler _persistent_storage_chunk_handlers[];
|
||||||
|
extern const ChunkHandler _signal_chunk_handlers[];
|
||||||
|
|
||||||
/** Array of all chunks in a savegame, \c NULL terminated. */
|
/** Array of all chunks in a savegame, \c NULL terminated. */
|
||||||
static const ChunkHandler * const _chunk_handlers[] = {
|
static const ChunkHandler * const _chunk_handlers[] = {
|
||||||
@@ -483,6 +485,7 @@ static const ChunkHandler * const _chunk_handlers[] = {
|
|||||||
_airport_chunk_handlers,
|
_airport_chunk_handlers,
|
||||||
_object_chunk_handlers,
|
_object_chunk_handlers,
|
||||||
_persistent_storage_chunk_handlers,
|
_persistent_storage_chunk_handlers,
|
||||||
|
_signal_chunk_handlers,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
286
src/saveload/signal_sl.cpp
Normal file
286
src/saveload/signal_sl.cpp
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
/* $Id$ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 signal_sl.cpp Code handling saving and loading of signals */
|
||||||
|
|
||||||
|
#include "../stdafx.h"
|
||||||
|
#include "../programmable_signals.h"
|
||||||
|
#include "../core/alloc_type.hpp"
|
||||||
|
#include "../core/bitmath_func.hpp"
|
||||||
|
#include <vector>
|
||||||
|
#include "saveload.h"
|
||||||
|
|
||||||
|
typedef std::vector<byte> Buffer;
|
||||||
|
|
||||||
|
// Variable length integers are stored in Variable Length Quantity
|
||||||
|
// format (http://en.wikipedia.org/wiki/Variable-length_quantity)
|
||||||
|
|
||||||
|
static void WriteVLI(Buffer &b, uint i)
|
||||||
|
{
|
||||||
|
uint lsmask = 0x7F;
|
||||||
|
uint msmask = ~0x7F;
|
||||||
|
while(i & msmask) {
|
||||||
|
byte part = (i & lsmask) | 0x80;
|
||||||
|
b.push_back(part);
|
||||||
|
i >>= 7;
|
||||||
|
}
|
||||||
|
b.push_back((byte) i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint ReadVLI()
|
||||||
|
{
|
||||||
|
uint shift = 0;
|
||||||
|
uint val = 0;
|
||||||
|
byte b;
|
||||||
|
|
||||||
|
b = SlReadByte();
|
||||||
|
while(b & 0x80) {
|
||||||
|
val |= uint(b & 0x7F) << shift;
|
||||||
|
shift += 7;
|
||||||
|
b = SlReadByte();
|
||||||
|
}
|
||||||
|
val |= uint(b) << shift;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteCondition(Buffer &b, SignalCondition *c)
|
||||||
|
{
|
||||||
|
WriteVLI(b, c->ConditionCode());
|
||||||
|
switch(c->ConditionCode()) {
|
||||||
|
case PSC_NUM_GREEN:
|
||||||
|
case PSC_NUM_RED: {
|
||||||
|
SignalVariableCondition *vc = static_cast<SignalVariableCondition*>(c);
|
||||||
|
WriteVLI(b, vc->comparator);
|
||||||
|
WriteVLI(b, vc->value);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case PSC_SIGNAL_STATE: {
|
||||||
|
SignalStateCondition *sc = static_cast<SignalStateCondition*>(c);
|
||||||
|
WriteVLI(b, sc->sig_tile);
|
||||||
|
WriteVLI(b, sc->sig_track);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SignalCondition *ReadCondition(SignalReference this_sig)
|
||||||
|
{
|
||||||
|
SignalConditionCode code = (SignalConditionCode) ReadVLI();
|
||||||
|
switch(code) {
|
||||||
|
case PSC_NUM_GREEN:
|
||||||
|
case PSC_NUM_RED: {
|
||||||
|
SignalVariableCondition *c = new SignalVariableCondition(code);
|
||||||
|
c->comparator = (SignalComparator) ReadVLI();
|
||||||
|
if(c->comparator > SGC_LAST) NOT_REACHED();
|
||||||
|
c->value = ReadVLI();
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PSC_SIGNAL_STATE: {
|
||||||
|
TileIndex ti = (TileIndex) ReadVLI();
|
||||||
|
Trackdir td = (Trackdir) ReadVLI();
|
||||||
|
return new SignalStateCondition(this_sig, ti, td);
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return new SignalSimpleCondition(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Save_SPRG()
|
||||||
|
{
|
||||||
|
// Check for, and dispose of, any signal information on a tile which doesn't have signals.
|
||||||
|
// This indicates that someone removed the signals from the tile but didn't clean them up.
|
||||||
|
// (This code is to detect bugs and limit their consquences, not to cover them up!)
|
||||||
|
for(ProgramList::iterator i = _signal_programs.begin(), e = _signal_programs.end();
|
||||||
|
i != e; ++i) {
|
||||||
|
SignalReference ref = i->first;
|
||||||
|
if(!HasProgrammableSignals(ref)) {
|
||||||
|
DEBUG(sl, 0, "Programmable signal information for (%x, %d) has been leaked!",
|
||||||
|
ref.tile, ref.track);
|
||||||
|
++i;
|
||||||
|
FreeSignalProgram(ref);
|
||||||
|
if(i == e) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OK, we can now write out our programs
|
||||||
|
Buffer b;
|
||||||
|
WriteVLI(b, _signal_programs.size());
|
||||||
|
for(ProgramList::iterator i = _signal_programs.begin(), e = _signal_programs.end();
|
||||||
|
i != e; ++i) {
|
||||||
|
SignalReference ref = i->first;
|
||||||
|
SignalProgram *prog = i->second;
|
||||||
|
|
||||||
|
prog->DebugPrintProgram();
|
||||||
|
|
||||||
|
WriteVLI(b, prog->tile);
|
||||||
|
WriteVLI(b, prog->track);
|
||||||
|
WriteVLI(b, prog->instructions.Length());
|
||||||
|
for(SignalInstruction **j = prog->instructions.Begin(), **je = prog->instructions.End();
|
||||||
|
j != je; ++j) {
|
||||||
|
SignalInstruction *insn = *j;
|
||||||
|
WriteVLI(b, insn->Opcode());
|
||||||
|
if(insn->Opcode() != PSO_FIRST)
|
||||||
|
WriteVLI(b, insn->Previous()->Id());
|
||||||
|
switch(insn->Opcode()) {
|
||||||
|
case PSO_FIRST: {
|
||||||
|
SignalSpecial *s = static_cast<SignalSpecial*>(insn);
|
||||||
|
WriteVLI(b, s->next->Id());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PSO_LAST: break;
|
||||||
|
|
||||||
|
case PSO_IF: {
|
||||||
|
SignalIf *i = static_cast<SignalIf*>(insn);
|
||||||
|
WriteCondition(b, i->condition);
|
||||||
|
WriteVLI(b, i->if_true->Id());
|
||||||
|
WriteVLI(b, i->if_false->Id());
|
||||||
|
WriteVLI(b, i->after->Id());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PSO_IF_ELSE:
|
||||||
|
case PSO_IF_ENDIF: {
|
||||||
|
SignalIf::PseudoInstruction *p = static_cast<SignalIf::PseudoInstruction*>(insn);
|
||||||
|
WriteVLI(b, p->block->Id());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PSO_SET_SIGNAL: {
|
||||||
|
SignalSet *s = static_cast<SignalSet*>(insn);
|
||||||
|
WriteVLI(b, s->next->Id());
|
||||||
|
WriteVLI(b, s->to_state ? 1 : 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint size = b.size();
|
||||||
|
SlSetLength(size);
|
||||||
|
for(uint i = 0; i < size; i++)
|
||||||
|
SlWriteByte(b[i]); // TODO Gotta be a better way
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't know the pointer values that need to be stored in various
|
||||||
|
// instruction fields at load time, so we need to instead store the IDs and
|
||||||
|
// then fix them up once all of the instructions have been loaded.
|
||||||
|
//
|
||||||
|
// Additionally, we store the opcode type we expect (if we expect a specific one)
|
||||||
|
// to check for consistency (For example, an If Pseudo Instruction's block should
|
||||||
|
// point at an If!)
|
||||||
|
struct Fixup {
|
||||||
|
Fixup(SignalInstruction **p, SignalOpcode type)
|
||||||
|
: type(type), ptr(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
SignalOpcode type;
|
||||||
|
SignalInstruction **ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef SmallVector<Fixup, 4> FixupList;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static void MakeFixup(FixupList &l, T *&ir, uint id, SignalOpcode op = PSO_INVALID)
|
||||||
|
{
|
||||||
|
ir = reinterpret_cast<T*>(id);
|
||||||
|
new(l.Append()) Fixup(reinterpret_cast<SignalInstruction**>(&ir), op);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DoFixups(FixupList &l, InstructionList &il)
|
||||||
|
{
|
||||||
|
for(Fixup *i = l.Begin(), *e = l.End(); i != e; ++i) {
|
||||||
|
uint id = reinterpret_cast<size_t>(*i->ptr);
|
||||||
|
if(id >= il.Length())
|
||||||
|
NOT_REACHED();
|
||||||
|
|
||||||
|
*i->ptr = il[id];
|
||||||
|
|
||||||
|
if(i->type != PSO_INVALID && (*i->ptr)->Opcode() != i->type) {
|
||||||
|
DEBUG(sl, 0, "Expected Id %d to be %d, but was in fact %d", id, i->type, (*i->ptr)->Opcode());
|
||||||
|
NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Load_SPRG()
|
||||||
|
{
|
||||||
|
uint count = ReadVLI();
|
||||||
|
for(uint i = 0; i < count; i++) {
|
||||||
|
FixupList l;
|
||||||
|
TileIndex tile = ReadVLI();
|
||||||
|
Track track = (Track) ReadVLI();
|
||||||
|
uint instructions = ReadVLI();
|
||||||
|
SignalReference ref(tile, track);
|
||||||
|
|
||||||
|
SignalProgram *sp = new SignalProgram(tile, track, true);
|
||||||
|
_signal_programs[ref] = sp;
|
||||||
|
|
||||||
|
for(uint j = 0; j < instructions; j++) {
|
||||||
|
SignalOpcode op = (SignalOpcode) ReadVLI();
|
||||||
|
switch(op) {
|
||||||
|
case PSO_FIRST: {
|
||||||
|
sp->first_instruction = new SignalSpecial(sp, PSO_FIRST);
|
||||||
|
sp->first_instruction->GetPrevHandle() = NULL;
|
||||||
|
MakeFixup(l, sp->first_instruction->next, ReadVLI());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PSO_LAST: {
|
||||||
|
sp->last_instruction = new SignalSpecial(sp, PSO_LAST);
|
||||||
|
sp->last_instruction->next = NULL;
|
||||||
|
MakeFixup(l, sp->last_instruction->GetPrevHandle(), ReadVLI());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PSO_IF: {
|
||||||
|
SignalIf *i = new SignalIf(sp, true);
|
||||||
|
MakeFixup(l, i->GetPrevHandle(), ReadVLI());
|
||||||
|
i->condition = ReadCondition(ref);
|
||||||
|
MakeFixup(l, i->if_true, ReadVLI());
|
||||||
|
MakeFixup(l, i->if_false, ReadVLI());
|
||||||
|
MakeFixup(l, i->after, ReadVLI());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PSO_IF_ELSE:
|
||||||
|
case PSO_IF_ENDIF: {
|
||||||
|
SignalIf::PseudoInstruction *p = new SignalIf::PseudoInstruction(sp, op);
|
||||||
|
MakeFixup(l, p->GetPrevHandle(), ReadVLI());
|
||||||
|
MakeFixup(l, p->block, ReadVLI(), PSO_IF);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PSO_SET_SIGNAL: {
|
||||||
|
SignalSet *s = new SignalSet(sp);
|
||||||
|
MakeFixup(l, s->GetPrevHandle(), ReadVLI());
|
||||||
|
MakeFixup(l, s->next, ReadVLI());
|
||||||
|
s->to_state = (SignalState) ReadVLI();
|
||||||
|
if(s->to_state > SIGNAL_STATE_MAX) NOT_REACHED();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DoFixups(l, sp->instructions);
|
||||||
|
sp->DebugPrintProgram();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern const ChunkHandler _signal_chunk_handlers[] = {
|
||||||
|
{ 'SPRG', Save_SPRG, Load_SPRG, NULL, NULL, CH_RIFF | CH_LAST},
|
||||||
|
};
|
@@ -1649,6 +1649,7 @@ static SettingsContainer &GetSettingsTree()
|
|||||||
limitations->Add(new SettingEntry("construction.road_stop_on_town_road"));
|
limitations->Add(new SettingEntry("construction.road_stop_on_town_road"));
|
||||||
limitations->Add(new SettingEntry("construction.road_stop_on_competitor_road"));
|
limitations->Add(new SettingEntry("construction.road_stop_on_competitor_road"));
|
||||||
limitations->Add(new SettingEntry("vehicle.disable_elrails"));
|
limitations->Add(new SettingEntry("vehicle.disable_elrails"));
|
||||||
|
limitations->Add(new SettingEntry("construction.maximum_signal_evaluations"));
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsPage *disasters = main->Add(new SettingsPage(STR_CONFIG_SETTING_ACCIDENTS));
|
SettingsPage *disasters = main->Add(new SettingsPage(STR_CONFIG_SETTING_ACCIDENTS));
|
||||||
|
@@ -311,6 +311,7 @@ struct ConstructionSettings {
|
|||||||
bool freeform_edges; ///< allow terraforming the tiles at the map edges
|
bool freeform_edges; ///< allow terraforming the tiles at the map edges
|
||||||
uint8 extra_tree_placement; ///< (dis)allow building extra trees in-game
|
uint8 extra_tree_placement; ///< (dis)allow building extra trees in-game
|
||||||
uint8 command_pause_level; ///< level/amount of commands that can't be executed while paused
|
uint8 command_pause_level; ///< level/amount of commands that can't be executed while paused
|
||||||
|
uint16 maximum_signal_evaluations; ///< maximum number of programmable signals which may be evaluated in one pass
|
||||||
|
|
||||||
uint32 terraform_per_64k_frames; ///< how many tile heights may, over a long period, be terraformed per 65536 frames?
|
uint32 terraform_per_64k_frames; ///< how many tile heights may, over a long period, be terraformed per 65536 frames?
|
||||||
uint16 terraform_frame_burst; ///< how many tile heights may, over a short period, be terraformed?
|
uint16 terraform_frame_burst; ///< how many tile heights may, over a short period, be terraformed?
|
||||||
|
230
src/signal.cpp
230
src/signal.cpp
@@ -17,9 +17,22 @@
|
|||||||
#include "viewport_func.h"
|
#include "viewport_func.h"
|
||||||
#include "train.h"
|
#include "train.h"
|
||||||
#include "company_base.h"
|
#include "company_base.h"
|
||||||
|
#include "gui.h"
|
||||||
|
#include "table/strings.h"
|
||||||
|
#include "programmable_signals.h"
|
||||||
|
#include "error.h"
|
||||||
|
|
||||||
#include "safeguards.h"
|
#include "safeguards.h"
|
||||||
|
|
||||||
|
/// List of signals dependent upon this one
|
||||||
|
typedef SmallVector<SignalReference, 4> SignalDependencyList;
|
||||||
|
|
||||||
|
/// Map of dependencies. The key identifies the signal,
|
||||||
|
/// the value is a list of all of the signals which depend upon that signal.
|
||||||
|
typedef std::map<SignalReference, SignalDependencyList> SignalDependencyMap;
|
||||||
|
|
||||||
|
static SignalDependencyMap _signal_dependencies;
|
||||||
|
static void MarkDependencidesForUpdate(SignalReference sig);
|
||||||
|
|
||||||
/** these are the maximums used for updating signal blocks */
|
/** these are the maximums used for updating signal blocks */
|
||||||
static const uint SIG_TBU_SIZE = 64; ///< number of signals entering to block
|
static const uint SIG_TBU_SIZE = 64; ///< number of signals entering to block
|
||||||
@@ -188,6 +201,7 @@ static SmallSet<Trackdir, SIG_TBU_SIZE> _tbuset("_tbuset"); ///< set of
|
|||||||
static SmallSet<DiagDirection, SIG_TBD_SIZE> _tbdset("_tbdset"); ///< set of open nodes in current signal block
|
static SmallSet<DiagDirection, SIG_TBD_SIZE> _tbdset("_tbdset"); ///< set of open nodes in current signal block
|
||||||
static SmallSet<DiagDirection, SIG_GLOB_SIZE> _globset("_globset"); ///< set of places to be updated in following runs
|
static SmallSet<DiagDirection, SIG_GLOB_SIZE> _globset("_globset"); ///< set of places to be updated in following runs
|
||||||
|
|
||||||
|
static uint _num_signals_evaluated; ///< Number of programmable signals evaluated
|
||||||
|
|
||||||
/** Check whether there is a train on rail, not in a depot */
|
/** Check whether there is a train on rail, not in a depot */
|
||||||
static Vehicle *TrainOnTileEnum(Vehicle *v, void *)
|
static Vehicle *TrainOnTileEnum(Vehicle *v, void *)
|
||||||
@@ -249,16 +263,23 @@ static inline bool MaybeAddToTodoSet(TileIndex t1, DiagDirection d1, TileIndex t
|
|||||||
enum SigFlags {
|
enum SigFlags {
|
||||||
SF_NONE = 0,
|
SF_NONE = 0,
|
||||||
SF_TRAIN = 1 << 0, ///< train found in segment
|
SF_TRAIN = 1 << 0, ///< train found in segment
|
||||||
SF_EXIT = 1 << 1, ///< exitsignal found
|
SF_FULL = 1 << 1, ///< some of buffers was full, do not continue
|
||||||
SF_EXIT2 = 1 << 2, ///< two or more exits found
|
SF_PBS = 1 << 2, ///< pbs signal found
|
||||||
SF_GREEN = 1 << 3, ///< green exitsignal found
|
|
||||||
SF_GREEN2 = 1 << 4, ///< two or more green exits found
|
|
||||||
SF_FULL = 1 << 5, ///< some of buffers was full, do not continue
|
|
||||||
SF_PBS = 1 << 6, ///< pbs signal found
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DECLARE_ENUM_AS_BIT_SET(SigFlags)
|
DECLARE_ENUM_AS_BIT_SET(SigFlags)
|
||||||
|
|
||||||
|
struct SigInfo {
|
||||||
|
inline SigInfo()
|
||||||
|
{
|
||||||
|
flags = SF_NONE;
|
||||||
|
num_exits = 0;
|
||||||
|
num_green = 0;
|
||||||
|
}
|
||||||
|
SigFlags flags;
|
||||||
|
uint num_exits;
|
||||||
|
uint num_green;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search signal block
|
* Search signal block
|
||||||
@@ -266,9 +287,9 @@ DECLARE_ENUM_AS_BIT_SET(SigFlags)
|
|||||||
* @param owner owner whose signals we are updating
|
* @param owner owner whose signals we are updating
|
||||||
* @return SigFlags
|
* @return SigFlags
|
||||||
*/
|
*/
|
||||||
static SigFlags ExploreSegment(Owner owner)
|
static SigInfo ExploreSegment(Owner owner)
|
||||||
{
|
{
|
||||||
SigFlags flags = SF_NONE;
|
SigInfo info;
|
||||||
|
|
||||||
TileIndex tile;
|
TileIndex tile;
|
||||||
DiagDirection enterdir;
|
DiagDirection enterdir;
|
||||||
@@ -283,13 +304,13 @@ static SigFlags ExploreSegment(Owner owner)
|
|||||||
|
|
||||||
if (IsRailDepot(tile)) {
|
if (IsRailDepot(tile)) {
|
||||||
if (enterdir == INVALID_DIAGDIR) { // from 'inside' - train just entered or left the depot
|
if (enterdir == INVALID_DIAGDIR) { // from 'inside' - train just entered or left the depot
|
||||||
if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
|
if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) info.flags |= SF_TRAIN;
|
||||||
exitdir = GetRailDepotDirection(tile);
|
exitdir = GetRailDepotDirection(tile);
|
||||||
tile += TileOffsByDiagDir(exitdir);
|
tile += TileOffsByDiagDir(exitdir);
|
||||||
enterdir = ReverseDiagDir(exitdir);
|
enterdir = ReverseDiagDir(exitdir);
|
||||||
break;
|
break;
|
||||||
} else if (enterdir == GetRailDepotDirection(tile)) { // entered a depot
|
} else if (enterdir == GetRailDepotDirection(tile)) { // entered a depot
|
||||||
if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
|
if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) info.flags |= SF_TRAIN;
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
@@ -303,10 +324,10 @@ static SigFlags ExploreSegment(Owner owner)
|
|||||||
if (tracks == TRACK_BIT_HORZ || tracks == TRACK_BIT_VERT) { // there is exactly one incidating track, no need to check
|
if (tracks == TRACK_BIT_HORZ || tracks == TRACK_BIT_VERT) { // there is exactly one incidating track, no need to check
|
||||||
tracks = tracks_masked;
|
tracks = tracks_masked;
|
||||||
/* If no train detected yet, and there is not no train -> there is a train -> set the flag */
|
/* If no train detected yet, and there is not no train -> there is a train -> set the flag */
|
||||||
if (!(flags & SF_TRAIN) && EnsureNoTrainOnTrackBits(tile, tracks).Failed()) flags |= SF_TRAIN;
|
if (!(info.flags & SF_TRAIN) && EnsureNoTrainOnTrackBits(tile, tracks).Failed()) info.flags |= SF_TRAIN;
|
||||||
} else {
|
} else {
|
||||||
if (tracks_masked == TRACK_BIT_NONE) continue; // no incidating track
|
if (tracks_masked == TRACK_BIT_NONE) continue; // no incidating track
|
||||||
if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
|
if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) info.flags |= SF_TRAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HasSignals(tile)) { // there is exactly one track - not zero, because there is exit from this tile
|
if (HasSignals(tile)) { // there is exactly one track - not zero, because there is exit from this tile
|
||||||
@@ -320,20 +341,19 @@ static SigFlags ExploreSegment(Owner owner)
|
|||||||
* (if it is a presignal EXIT and it changes, it will be added to 'to-be-done' set later) */
|
* (if it is a presignal EXIT and it changes, it will be added to 'to-be-done' set later) */
|
||||||
if (HasSignalOnTrackdir(tile, reversedir)) {
|
if (HasSignalOnTrackdir(tile, reversedir)) {
|
||||||
if (IsPbsSignal(sig)) {
|
if (IsPbsSignal(sig)) {
|
||||||
flags |= SF_PBS;
|
info.flags |= SF_PBS;
|
||||||
} else if (!_tbuset.Add(tile, reversedir)) {
|
} else if (!_tbuset.Add(tile, reversedir)) {
|
||||||
return flags | SF_FULL;
|
info.flags |= SF_FULL;
|
||||||
|
return info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (HasSignalOnTrackdir(tile, trackdir) && !IsOnewaySignal(tile, track)) flags |= SF_PBS;
|
if (HasSignalOnTrackdir(tile, trackdir) && !IsOnewaySignal(tile, track)) info.flags |= SF_PBS;
|
||||||
|
|
||||||
/* if it is a presignal EXIT in OUR direction and we haven't found 2 green exits yes, do special check */
|
/* if it is a presignal EXIT in OUR direction, count it */
|
||||||
if (!(flags & SF_GREEN2) && IsPresignalExit(tile, track) && HasSignalOnTrackdir(tile, trackdir)) { // found presignal exit
|
if (IsPresignalExit(tile, track) && HasSignalOnTrackdir(tile, trackdir)) { // found presignal exit
|
||||||
if (flags & SF_EXIT) flags |= SF_EXIT2; // found two (or more) exits
|
info.num_exits++;
|
||||||
flags |= SF_EXIT; // found at least one exit - allow for compiler optimizations
|
|
||||||
if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_GREEN) { // found green presignal exit
|
if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_GREEN) { // found green presignal exit
|
||||||
if (flags & SF_GREEN) flags |= SF_GREEN2;
|
info.num_green++;
|
||||||
flags |= SF_GREEN;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,7 +365,10 @@ static SigFlags ExploreSegment(Owner owner)
|
|||||||
if (dir != enterdir && (tracks & _enterdir_to_trackbits[dir])) { // any track incidating?
|
if (dir != enterdir && (tracks & _enterdir_to_trackbits[dir])) { // any track incidating?
|
||||||
TileIndex newtile = tile + TileOffsByDiagDir(dir); // new tile to check
|
TileIndex newtile = tile + TileOffsByDiagDir(dir); // new tile to check
|
||||||
DiagDirection newdir = ReverseDiagDir(dir); // direction we are entering from
|
DiagDirection newdir = ReverseDiagDir(dir); // direction we are entering from
|
||||||
if (!MaybeAddToTodoSet(newtile, newdir, tile, dir)) return flags | SF_FULL;
|
if (!MaybeAddToTodoSet(newtile, newdir, tile, dir)) {
|
||||||
|
info.flags |= SF_FULL;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,7 +381,7 @@ static SigFlags ExploreSegment(Owner owner)
|
|||||||
if (DiagDirToAxis(enterdir) != GetRailStationAxis(tile)) continue; // different axis
|
if (DiagDirToAxis(enterdir) != GetRailStationAxis(tile)) continue; // different axis
|
||||||
if (IsStationTileBlocked(tile)) continue; // 'eye-candy' station tile
|
if (IsStationTileBlocked(tile)) continue; // 'eye-candy' station tile
|
||||||
|
|
||||||
if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
|
if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) info.flags |= SF_TRAIN;
|
||||||
tile += TileOffsByDiagDir(exitdir);
|
tile += TileOffsByDiagDir(exitdir);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -367,7 +390,7 @@ static SigFlags ExploreSegment(Owner owner)
|
|||||||
if (GetTileOwner(tile) != owner) continue;
|
if (GetTileOwner(tile) != owner) continue;
|
||||||
if (DiagDirToAxis(enterdir) == GetCrossingRoadAxis(tile)) continue; // different axis
|
if (DiagDirToAxis(enterdir) == GetCrossingRoadAxis(tile)) continue; // different axis
|
||||||
|
|
||||||
if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
|
if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) info.flags |= SF_TRAIN;
|
||||||
tile += TileOffsByDiagDir(exitdir);
|
tile += TileOffsByDiagDir(exitdir);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -377,13 +400,13 @@ static SigFlags ExploreSegment(Owner owner)
|
|||||||
DiagDirection dir = GetTunnelBridgeDirection(tile);
|
DiagDirection dir = GetTunnelBridgeDirection(tile);
|
||||||
|
|
||||||
if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole
|
if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole
|
||||||
if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
|
if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) info.flags |= SF_TRAIN;
|
||||||
enterdir = dir;
|
enterdir = dir;
|
||||||
exitdir = ReverseDiagDir(dir);
|
exitdir = ReverseDiagDir(dir);
|
||||||
tile += TileOffsByDiagDir(exitdir); // just skip to next tile
|
tile += TileOffsByDiagDir(exitdir); // just skip to next tile
|
||||||
} else { // NOT incoming from the wormhole!
|
} else { // NOT incoming from the wormhole!
|
||||||
if (ReverseDiagDir(enterdir) != dir) continue;
|
if (ReverseDiagDir(enterdir) != dir) continue;
|
||||||
if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
|
if (!(info.flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) info.flags |= SF_TRAIN;
|
||||||
tile = GetOtherTunnelBridgeEnd(tile); // just skip to exit tile
|
tile = GetOtherTunnelBridgeEnd(tile); // just skip to exit tile
|
||||||
enterdir = INVALID_DIAGDIR;
|
enterdir = INVALID_DIAGDIR;
|
||||||
exitdir = INVALID_DIAGDIR;
|
exitdir = INVALID_DIAGDIR;
|
||||||
@@ -395,10 +418,12 @@ static SigFlags ExploreSegment(Owner owner)
|
|||||||
continue; // continue the while() loop
|
continue; // continue the while() loop
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!MaybeAddToTodoSet(tile, enterdir, oldtile, exitdir)) return flags | SF_FULL;
|
if (!MaybeAddToTodoSet(tile, enterdir, oldtile, exitdir)) {
|
||||||
|
info.flags |= SF_FULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return flags;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -407,43 +432,67 @@ static SigFlags ExploreSegment(Owner owner)
|
|||||||
*
|
*
|
||||||
* @param flags info about segment
|
* @param flags info about segment
|
||||||
*/
|
*/
|
||||||
static void UpdateSignalsAroundSegment(SigFlags flags)
|
static void UpdateSignalsAroundSegment(SigInfo info)
|
||||||
{
|
{
|
||||||
TileIndex tile;
|
TileIndex tile;
|
||||||
Trackdir trackdir;
|
Trackdir trackdir;
|
||||||
|
Track track;
|
||||||
|
|
||||||
while (_tbuset.Get(&tile, &trackdir)) {
|
while (_tbuset.Get(&tile, &trackdir)) {
|
||||||
assert(HasSignalOnTrackdir(tile, trackdir));
|
assert(HasSignalOnTrackdir(tile, trackdir));
|
||||||
|
|
||||||
SignalType sig = GetSignalType(tile, TrackdirToTrack(trackdir));
|
track = TrackdirToTrack(trackdir);
|
||||||
|
SignalType sig = GetSignalType(tile, track);
|
||||||
SignalState newstate = SIGNAL_STATE_GREEN;
|
SignalState newstate = SIGNAL_STATE_GREEN;
|
||||||
|
|
||||||
/* determine whether the new state is red */
|
/* determine whether the new state is red */
|
||||||
if (flags & SF_TRAIN) {
|
if (info.flags & SF_TRAIN) {
|
||||||
/* train in the segment */
|
/* train in the segment */
|
||||||
newstate = SIGNAL_STATE_RED;
|
newstate = SIGNAL_STATE_RED;
|
||||||
|
} else if (sig == SIGTYPE_PROG &&
|
||||||
|
_num_signals_evaluated > _settings_game.construction.maximum_signal_evaluations) {
|
||||||
|
/* too many cascades */
|
||||||
|
newstate = SIGNAL_STATE_RED;
|
||||||
} else {
|
} else {
|
||||||
/* is it a bidir combo? - then do not count its other signal direction as exit */
|
/* is it a bidir combo? - then do not count its other signal direction as exit */
|
||||||
if (sig == SIGTYPE_COMBO && HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir))) {
|
if (IsComboSignal(sig) && HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir))) {
|
||||||
/* at least one more exit */
|
// Don't count ourselves
|
||||||
if ((flags & SF_EXIT2) &&
|
uint exits = info.num_exits - 1;
|
||||||
/* no green exit */
|
uint green = info.num_green;
|
||||||
(!(flags & SF_GREEN) ||
|
if (GetSignalStateByTrackdir(tile, ReverseTrackdir(trackdir)) == SIGNAL_STATE_GREEN)
|
||||||
/* only one green exit, and it is this one - so all other exits are red */
|
green--;
|
||||||
(!(flags & SF_GREEN2) && GetSignalStateByTrackdir(tile, ReverseTrackdir(trackdir)) == SIGNAL_STATE_GREEN))) {
|
|
||||||
|
if (sig == SIGTYPE_PROG) { /* Programmable */
|
||||||
|
_num_signals_evaluated++;
|
||||||
|
|
||||||
|
if (!RunSignalProgram(SignalReference(tile, track), exits, green))
|
||||||
|
newstate = SIGNAL_STATE_RED;
|
||||||
|
} else { /* traditional combo */
|
||||||
|
if (!green && exits)
|
||||||
newstate = SIGNAL_STATE_RED;
|
newstate = SIGNAL_STATE_RED;
|
||||||
}
|
}
|
||||||
} else { // entry, at least one exit, no green exit
|
} else { // entry, at least one exit, no green exit
|
||||||
if (IsPresignalEntry(tile, TrackdirToTrack(trackdir)) && (flags & SF_EXIT) && !(flags & SF_GREEN)) newstate = SIGNAL_STATE_RED;
|
if (IsEntrySignal(sig)) {
|
||||||
|
if (sig == SIGTYPE_PROG) {
|
||||||
|
_num_signals_evaluated++;
|
||||||
|
if (!RunSignalProgram(SignalReference(tile, track), info.num_exits, info.num_green))
|
||||||
|
newstate = SIGNAL_STATE_RED;
|
||||||
|
} else { /* traditional combo */
|
||||||
|
if (!info.num_green && info.num_exits) newstate = SIGNAL_STATE_RED;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* only when the state changes */
|
/* only when the state changes */
|
||||||
if (newstate != GetSignalStateByTrackdir(tile, trackdir)) {
|
if (newstate != GetSignalStateByTrackdir(tile, trackdir)) {
|
||||||
if (IsPresignalExit(tile, TrackdirToTrack(trackdir))) {
|
if (IsExitSignal(sig)) {
|
||||||
/* for pre-signal exits, add block to the global set */
|
/* for pre-signal exits, add block to the global set */
|
||||||
DiagDirection exitdir = TrackdirToExitdir(ReverseTrackdir(trackdir));
|
DiagDirection exitdir = TrackdirToExitdir(ReverseTrackdir(trackdir));
|
||||||
_globset.Add(tile, exitdir); // do not check for full global set, first update all signals
|
_globset.Add(tile, exitdir); // do not check for full global set, first update all signals
|
||||||
|
|
||||||
|
// Progsig dependencies
|
||||||
|
MarkDependencidesForUpdate(SignalReference(tile, track));
|
||||||
}
|
}
|
||||||
SetSignalStateByTrackdir(tile, trackdir, newstate);
|
SetSignalStateByTrackdir(tile, trackdir, newstate);
|
||||||
MarkTileDirtyByTile(tile);
|
MarkTileDirtyByTile(tile);
|
||||||
@@ -475,6 +524,7 @@ static SigSegState UpdateSignalsInBuffer(Owner owner)
|
|||||||
|
|
||||||
bool first = true; // first block?
|
bool first = true; // first block?
|
||||||
SigSegState state = SIGSEG_FREE; // value to return
|
SigSegState state = SIGSEG_FREE; // value to return
|
||||||
|
_num_signals_evaluated = 0;
|
||||||
|
|
||||||
TileIndex tile;
|
TileIndex tile;
|
||||||
DiagDirection dir;
|
DiagDirection dir;
|
||||||
@@ -529,25 +579,29 @@ static SigSegState UpdateSignalsInBuffer(Owner owner)
|
|||||||
assert(!_tbdset.Overflowed()); // it really shouldn't overflow by these one or two items
|
assert(!_tbdset.Overflowed()); // it really shouldn't overflow by these one or two items
|
||||||
assert(!_tbdset.IsEmpty()); // it wouldn't hurt anyone, but shouldn't happen too
|
assert(!_tbdset.IsEmpty()); // it wouldn't hurt anyone, but shouldn't happen too
|
||||||
|
|
||||||
SigFlags flags = ExploreSegment(owner);
|
SigInfo info = ExploreSegment(owner);
|
||||||
|
|
||||||
if (first) {
|
if (first) {
|
||||||
first = false;
|
first = false;
|
||||||
/* SIGSEG_FREE is set by default */
|
/* SIGSEG_FREE is set by default */
|
||||||
if (flags & SF_PBS) {
|
if (info.flags & SF_PBS) {
|
||||||
state = SIGSEG_PBS;
|
state = SIGSEG_PBS;
|
||||||
} else if ((flags & SF_TRAIN) || ((flags & SF_EXIT) && !(flags & SF_GREEN)) || (flags & SF_FULL)) {
|
} else if ((info.flags & SF_TRAIN) || ((info.num_exits) && !(info.num_green)) || (info.flags & SF_FULL)) {
|
||||||
state = SIGSEG_FULL;
|
state = SIGSEG_FULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* do not do anything when some buffer was full */
|
/* do not do anything when some buffer was full */
|
||||||
if (flags & SF_FULL) {
|
if (info.flags & SF_FULL) {
|
||||||
ResetSets(); // free all sets
|
ResetSets(); // free all sets
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateSignalsAroundSegment(flags);
|
if (_num_signals_evaluated > _settings_game.construction.maximum_signal_evaluations) {
|
||||||
|
ShowErrorMessage(STR_ERROR_SIGNAL_CHANGES, STR_EMPTY, WL_INFO);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateSignalsAroundSegment(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
@@ -660,3 +714,89 @@ void SetSignalsOnBothDir(TileIndex tile, Track track, Owner owner)
|
|||||||
AddTrackToSignalBuffer(tile, track, owner);
|
AddTrackToSignalBuffer(tile, track, owner);
|
||||||
UpdateSignalsInBuffer(owner);
|
UpdateSignalsInBuffer(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AddSignalDependency(SignalReference on, SignalReference dep)
|
||||||
|
{
|
||||||
|
assert(GetTileOwner(on.tile) == GetTileOwner(dep.tile));
|
||||||
|
SignalDependencyList &dependencies = _signal_dependencies[on];
|
||||||
|
(*dependencies.Append()) = dep;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveSignalDependency(SignalReference on, SignalReference dep)
|
||||||
|
{
|
||||||
|
SignalDependencyList &dependencies = _signal_dependencies[on];
|
||||||
|
SignalReference *ob = dependencies.Find(dep);
|
||||||
|
|
||||||
|
// Destroying both signals in same command
|
||||||
|
if(ob == dependencies.End())
|
||||||
|
return;
|
||||||
|
|
||||||
|
dependencies.Erase(ob);
|
||||||
|
if (dependencies.Length() == 0)
|
||||||
|
_signal_dependencies.erase(on);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeSignalDependencies()
|
||||||
|
{
|
||||||
|
_signal_dependencies.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MarkDependencidesForUpdate(SignalReference on)
|
||||||
|
{
|
||||||
|
SignalDependencyMap::iterator f = _signal_dependencies.find(on);
|
||||||
|
if (f == _signal_dependencies.end()) return;
|
||||||
|
|
||||||
|
SignalDependencyList &dependencies = f->second;
|
||||||
|
for (SignalReference *i = dependencies.Begin(), *e = dependencies.End();
|
||||||
|
i != e; ++i) {
|
||||||
|
assert(GetTileOwner(i->tile) == GetTileOwner(on.tile));
|
||||||
|
|
||||||
|
Trackdir td = TrackToTrackdir(i->track);
|
||||||
|
_globset.Add(i->tile, TrackdirToExitdir(td));
|
||||||
|
_globset.Add(i->tile, TrackdirToExitdir(ReverseTrackdir(td)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckRemoveSignalsFromTile(TileIndex tile)
|
||||||
|
{
|
||||||
|
if (!HasSignals(tile)) return;
|
||||||
|
|
||||||
|
TrackBits tb = GetTrackBits(tile);
|
||||||
|
Track tr;
|
||||||
|
while ((tr = RemoveFirstTrack(&tb)) != INVALID_TRACK) {
|
||||||
|
if (HasSignalOnTrack(tile, tr)) CheckRemoveSignal(tile, tr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NotifyRemovingDependentSignal(SignalReference on, SignalReference by)
|
||||||
|
{
|
||||||
|
SignalType t = GetSignalType(by.tile, by.track);
|
||||||
|
if (IsProgrammableSignal(t)) {
|
||||||
|
RemoveProgramDependencies(by, on);
|
||||||
|
} else DEBUG(misc, 0, "Removing dependency held by non-programmable signal (Unexpected)");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckRemoveSignal(TileIndex tile, Track track)
|
||||||
|
{
|
||||||
|
if (!HasSignalOnTrack(tile, track)) return;
|
||||||
|
SignalReference thisRef(tile, track);
|
||||||
|
|
||||||
|
SignalType t = GetSignalType(tile, track);
|
||||||
|
if (IsProgrammableSignal(t)) {
|
||||||
|
FreeSignalProgram(thisRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalDependencyMap::iterator i = _signal_dependencies.find(SignalReference(tile, track)),
|
||||||
|
e = _signal_dependencies.end();
|
||||||
|
|
||||||
|
if (i != e) {
|
||||||
|
SignalDependencyList &dependencies = i->second;
|
||||||
|
|
||||||
|
for (SignalReference *ir = dependencies.Begin(), *er = dependencies.End(); ir != er; ++ir) {
|
||||||
|
assert(GetTileOwner(ir->tile) == GetTileOwner(tile));
|
||||||
|
NotifyRemovingDependentSignal(thisRef, *ir);
|
||||||
|
}
|
||||||
|
|
||||||
|
_signal_dependencies.erase(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -12,10 +12,12 @@
|
|||||||
#ifndef SIGNAL_FUNC_H
|
#ifndef SIGNAL_FUNC_H
|
||||||
#define SIGNAL_FUNC_H
|
#define SIGNAL_FUNC_H
|
||||||
|
|
||||||
|
#include "signal_type.h"
|
||||||
#include "track_type.h"
|
#include "track_type.h"
|
||||||
#include "tile_type.h"
|
#include "tile_type.h"
|
||||||
#include "direction_type.h"
|
#include "direction_type.h"
|
||||||
#include "company_type.h"
|
#include "company_type.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps a trackdir to the bit that stores its status in the map arrays, in the
|
* Maps a trackdir to the bit that stores its status in the map arrays, in the
|
||||||
@@ -47,6 +49,61 @@ static inline byte SignalOnTrack(Track track)
|
|||||||
return _signal_on_track[track];
|
return _signal_on_track[track];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is a given signal type a presignal entry signal?
|
||||||
|
static inline bool IsEntrySignal(SignalType type)
|
||||||
|
{
|
||||||
|
return type == SIGTYPE_ENTRY || type == SIGTYPE_COMBO || type == SIGTYPE_PROG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is a given signal type a presignal exit signal?
|
||||||
|
static inline bool IsExitSignal(SignalType type)
|
||||||
|
{
|
||||||
|
return type == SIGTYPE_EXIT || type == SIGTYPE_COMBO || type == SIGTYPE_PROG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is a given signal type a presignal combo signal?
|
||||||
|
static inline bool IsComboSignal(SignalType type)
|
||||||
|
{
|
||||||
|
return type == SIGTYPE_COMBO || type == SIGTYPE_PROG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is a given signal type a PBS signal?
|
||||||
|
static inline bool IsPbsSignal(SignalType type)
|
||||||
|
{
|
||||||
|
return type == SIGTYPE_PBS || type == SIGTYPE_PBS_ONEWAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is this a programmable signal?
|
||||||
|
static inline bool IsProgrammableSignal(SignalType type)
|
||||||
|
{
|
||||||
|
return type == SIGTYPE_PROG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Does a given signal have a PBS sprite?
|
||||||
|
static inline bool IsSignalSpritePBS(SignalType type)
|
||||||
|
{
|
||||||
|
return type >= SIGTYPE_FIRST_PBS_SPRITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline SignalType NextSignalType(SignalType cur, uint which_signals)
|
||||||
|
{
|
||||||
|
bool pbs = (which_signals != 0);
|
||||||
|
bool block = (which_signals != 1);
|
||||||
|
|
||||||
|
switch(cur) {
|
||||||
|
case SIGTYPE_NORMAL: return block ? SIGTYPE_ENTRY : SIGTYPE_PBS;
|
||||||
|
case SIGTYPE_ENTRY: return block ? SIGTYPE_EXIT : SIGTYPE_PBS;
|
||||||
|
case SIGTYPE_EXIT: return block ? SIGTYPE_COMBO : SIGTYPE_PBS;
|
||||||
|
case SIGTYPE_COMBO: return block ? SIGTYPE_PROG : SIGTYPE_PBS;
|
||||||
|
case SIGTYPE_PROG: return pbs ? SIGTYPE_PBS : SIGTYPE_NORMAL;
|
||||||
|
case SIGTYPE_PBS: return pbs ? SIGTYPE_PBS_ONEWAY : SIGTYPE_NORMAL;
|
||||||
|
case SIGTYPE_PBS_ONEWAY: return block ? SIGTYPE_NORMAL : SIGTYPE_PBS;
|
||||||
|
default:
|
||||||
|
DEBUG(map, 0, "Attempt to cycle from signal type %d", cur);
|
||||||
|
return SIGTYPE_NORMAL; // Fortunately mostly harmless
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** State of the signal segment */
|
/** State of the signal segment */
|
||||||
enum SigSegState {
|
enum SigSegState {
|
||||||
SIGSEG_FREE, ///< Free and has no pre-signal exits or at least one green exit
|
SIGSEG_FREE, ///< Free and has no pre-signal exits or at least one green exit
|
||||||
@@ -54,6 +111,30 @@ enum SigSegState {
|
|||||||
SIGSEG_PBS, ///< Segment is a PBS segment
|
SIGSEG_PBS, ///< Segment is a PBS segment
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Checks for any data attached to any signals, and removes it. Call when performing
|
||||||
|
* an action which may potentially remove signals from a tile, in order to avoid leaking
|
||||||
|
* data.
|
||||||
|
*/
|
||||||
|
void CheckRemoveSignalsFromTile(TileIndex tile);
|
||||||
|
|
||||||
|
/** Checks for, and removes, any extra signal data. Call when removing a piece of track
|
||||||
|
* which is potentially signalled, in order to free any extra data that may be associated
|
||||||
|
* with said track.
|
||||||
|
*/
|
||||||
|
void CheckRemoveSignal(TileIndex tile, Track track);
|
||||||
|
|
||||||
|
/** Adds a signal dependency
|
||||||
|
* The signal identified by @p dep will be marked as dependend upon
|
||||||
|
* the signal identified by @p on
|
||||||
|
*/
|
||||||
|
void AddSignalDependency(SignalReference on, SignalReference dep);
|
||||||
|
|
||||||
|
/// Removes a signal dependency. Arguments same as AddSignalDependency
|
||||||
|
void RemoveSignalDependency(SignalReference on, SignalReference dep);
|
||||||
|
|
||||||
|
/// Frees signal dependencies (for newgame/load)
|
||||||
|
void FreeSignalDependencies();
|
||||||
|
|
||||||
SigSegState UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner);
|
SigSegState UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner);
|
||||||
void SetSignalsOnBothDir(TileIndex tile, Track track, Owner owner);
|
void SetSignalsOnBothDir(TileIndex tile, Track track, Owner owner);
|
||||||
void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner);
|
void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner);
|
||||||
|
@@ -13,6 +13,8 @@
|
|||||||
#define SIGNAL_TYPE_H
|
#define SIGNAL_TYPE_H
|
||||||
|
|
||||||
#include "core/enum_type.hpp"
|
#include "core/enum_type.hpp"
|
||||||
|
#include "track_type.h"
|
||||||
|
#include "tile_type.h"
|
||||||
|
|
||||||
/** Variant of the signal, i.e. how does the signal look? */
|
/** Variant of the signal, i.e. how does the signal look? */
|
||||||
enum SignalVariant {
|
enum SignalVariant {
|
||||||
@@ -29,14 +31,28 @@ enum SignalType {
|
|||||||
SIGTYPE_COMBO = 3, ///< presignal inter-block
|
SIGTYPE_COMBO = 3, ///< presignal inter-block
|
||||||
SIGTYPE_PBS = 4, ///< normal pbs signal
|
SIGTYPE_PBS = 4, ///< normal pbs signal
|
||||||
SIGTYPE_PBS_ONEWAY = 5, ///< no-entry signal
|
SIGTYPE_PBS_ONEWAY = 5, ///< no-entry signal
|
||||||
|
SIGTYPE_PROG = 6, ///< programmable presignal
|
||||||
|
|
||||||
SIGTYPE_END,
|
SIGTYPE_END,
|
||||||
SIGTYPE_LAST = SIGTYPE_PBS_ONEWAY,
|
SIGTYPE_LAST = SIGTYPE_PROG,
|
||||||
SIGTYPE_LAST_NOPBS = SIGTYPE_COMBO,
|
SIGTYPE_FIRST_PBS_SPRITE = SIGTYPE_PBS,
|
||||||
};
|
};
|
||||||
/** Helper information for extract tool. */
|
/** Helper information for extract tool. */
|
||||||
template <> struct EnumPropsT<SignalType> : MakeEnumPropsT<SignalType, byte, SIGTYPE_NORMAL, SIGTYPE_END, SIGTYPE_END, 3> {};
|
template <> struct EnumPropsT<SignalType> : MakeEnumPropsT<SignalType, byte, SIGTYPE_NORMAL, SIGTYPE_END, SIGTYPE_END, 3> {};
|
||||||
|
|
||||||
|
/** Reference to a signal
|
||||||
|
*
|
||||||
|
* A reference to a signal by its tile and track
|
||||||
|
*/
|
||||||
|
struct SignalReference {
|
||||||
|
inline SignalReference(TileIndex t, Track tr) : tile(t), track(tr) {}
|
||||||
|
inline bool operator<(const SignalReference& o) const { return tile < o.tile || (tile == o.tile && track < o.track); }
|
||||||
|
inline bool operator==(const SignalReference& o) const { return tile == o.tile && track == o.track; }
|
||||||
|
inline bool operator!=(const SignalReference& o) const { return tile != o.tile || track != o.track; }
|
||||||
|
|
||||||
|
TileIndex tile;
|
||||||
|
Track track;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* These are states in which a signal can be. Currently these are only two, so
|
* These are states in which a signal can be. Currently these are only two, so
|
||||||
@@ -46,6 +62,7 @@ template <> struct EnumPropsT<SignalType> : MakeEnumPropsT<SignalType, byte, SIG
|
|||||||
enum SignalState {
|
enum SignalState {
|
||||||
SIGNAL_STATE_RED = 0, ///< The signal is red
|
SIGNAL_STATE_RED = 0, ///< The signal is red
|
||||||
SIGNAL_STATE_GREEN = 1, ///< The signal is green
|
SIGNAL_STATE_GREEN = 1, ///< The signal is green
|
||||||
|
SIGNAL_STATE_MAX = SIGNAL_STATE_GREEN,
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* SIGNAL_TYPE_H */
|
#endif /* SIGNAL_TYPE_H */
|
||||||
|
@@ -1458,6 +1458,21 @@ from = 77
|
|||||||
def = true
|
def = true
|
||||||
cat = SC_EXPERT
|
cat = SC_EXPERT
|
||||||
|
|
||||||
|
[SDT_VAR]
|
||||||
|
base = GameSettings
|
||||||
|
var = construction.maximum_signal_evaluations
|
||||||
|
type = SLE_UINT16
|
||||||
|
from = 200
|
||||||
|
to = SL_MAX_VERSION
|
||||||
|
def = 256
|
||||||
|
min = 64
|
||||||
|
max = 4096
|
||||||
|
interval = 1
|
||||||
|
str = STR_CONFIG_SETTING_MAX_SIGNAL_EVALUATIONS
|
||||||
|
strhelp = STR_CONFIG_SETTING_MAX_SIGNAL_EVALUATIONS_HELPTEXT
|
||||||
|
strval = STR_JUST_COMMA
|
||||||
|
cat = SC_EXPERT
|
||||||
|
|
||||||
; previously ai-new setting.
|
; previously ai-new setting.
|
||||||
[SDT_NULL]
|
[SDT_NULL]
|
||||||
length = 1
|
length = 1
|
||||||
|
@@ -296,8 +296,12 @@ static const uint16 EMPTY_BOUNDING_BOX_SPRITE_COUNT = 1;
|
|||||||
static const SpriteID SPR_PALETTE_BASE = SPR_EMPTY_BOUNDING_BOX + EMPTY_BOUNDING_BOX_SPRITE_COUNT;
|
static const SpriteID SPR_PALETTE_BASE = SPR_EMPTY_BOUNDING_BOX + EMPTY_BOUNDING_BOX_SPRITE_COUNT;
|
||||||
static const uint16 PALETTE_SPRITE_COUNT = 1;
|
static const uint16 PALETTE_SPRITE_COUNT = 1;
|
||||||
|
|
||||||
|
/* Programmable signal sprites */
|
||||||
|
static const SpriteID SPR_PROGSIGNAL_BASE = SPR_PALETTE_BASE + PALETTE_SPRITE_COUNT;
|
||||||
|
static const uint16 PROGSIGNAL_SPRITE_COUNT = 32;
|
||||||
|
|
||||||
/* From where can we start putting NewGRFs? */
|
/* From where can we start putting NewGRFs? */
|
||||||
static const SpriteID SPR_NEWGRFS_BASE = SPR_PALETTE_BASE + PALETTE_SPRITE_COUNT;
|
static const SpriteID SPR_NEWGRFS_BASE = SPR_PROGSIGNAL_BASE + PROGSIGNAL_SPRITE_COUNT;
|
||||||
|
|
||||||
/* Manager face sprites */
|
/* Manager face sprites */
|
||||||
static const SpriteID SPR_GRADIENT = 874; // background gradient behind manager face
|
static const SpriteID SPR_GRADIENT = 874; // background gradient behind manager face
|
||||||
@@ -1309,12 +1313,14 @@ static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_EXIT = SPR_SIGNALS_BASE + 28;
|
|||||||
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_COMBO = SPR_SIGNALS_BASE + 44;
|
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_COMBO = SPR_SIGNALS_BASE + 44;
|
||||||
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_PBS = SPR_SIGNALS_BASE + 124;
|
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_PBS = SPR_SIGNALS_BASE + 124;
|
||||||
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_PBS_OWAY = SPR_SIGNALS_BASE + 140;
|
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_PBS_OWAY = SPR_SIGNALS_BASE + 140;
|
||||||
|
static const SpriteID SPR_IMG_SIGNAL_ELECTRIC_PROG = SPR_PROGSIGNAL_BASE + 28;
|
||||||
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_NORM = SPR_SIGNALS_BASE + 60;
|
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_NORM = SPR_SIGNALS_BASE + 60;
|
||||||
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_ENTRY = SPR_SIGNALS_BASE + 76;
|
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_ENTRY = SPR_SIGNALS_BASE + 76;
|
||||||
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_EXIT = SPR_SIGNALS_BASE + 92;
|
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_EXIT = SPR_SIGNALS_BASE + 92;
|
||||||
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_COMBO = SPR_SIGNALS_BASE + 108;
|
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_COMBO = SPR_SIGNALS_BASE + 108;
|
||||||
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_PBS = SPR_SIGNALS_BASE + 188;
|
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_PBS = SPR_SIGNALS_BASE + 188;
|
||||||
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_PBS_OWAY= SPR_SIGNALS_BASE + 204;
|
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_PBS_OWAY= SPR_SIGNALS_BASE + 204;
|
||||||
|
static const SpriteID SPR_IMG_SIGNAL_SEMAPHORE_PROG = SPR_PROGSIGNAL_BASE + 12;
|
||||||
static const SpriteID SPR_IMG_SIGNAL_CONVERT = SPR_OPENTTD_BASE + 135;
|
static const SpriteID SPR_IMG_SIGNAL_CONVERT = SPR_OPENTTD_BASE + 135;
|
||||||
|
|
||||||
static const SpriteID SPR_IMG_TUNNEL_RAIL = 2430;
|
static const SpriteID SPR_IMG_TUNNEL_RAIL = 2430;
|
||||||
|
@@ -82,15 +82,18 @@ enum BuildSignalWidgets {
|
|||||||
WID_BS_SEMAPHORE_ENTRY, ///< Build a semaphore entry block signal
|
WID_BS_SEMAPHORE_ENTRY, ///< Build a semaphore entry block signal
|
||||||
WID_BS_SEMAPHORE_EXIT, ///< Build a semaphore exit block signal
|
WID_BS_SEMAPHORE_EXIT, ///< Build a semaphore exit block signal
|
||||||
WID_BS_SEMAPHORE_COMBO, ///< Build a semaphore combo block signal
|
WID_BS_SEMAPHORE_COMBO, ///< Build a semaphore combo block signal
|
||||||
|
WID_BS_SEMAPHORE_PROG, ///< Build a semahore programmable signal
|
||||||
WID_BS_SEMAPHORE_PBS, ///< Build a semaphore path signal.
|
WID_BS_SEMAPHORE_PBS, ///< Build a semaphore path signal.
|
||||||
WID_BS_SEMAPHORE_PBS_OWAY, ///< Build a semaphore one way path signal.
|
WID_BS_SEMAPHORE_PBS_OWAY, ///< Build a semaphore one way path signal.
|
||||||
WID_BS_ELECTRIC_NORM, ///< Build an electric normal block signal
|
WID_BS_ELECTRIC_NORM, ///< Build an electric normal block signal
|
||||||
WID_BS_ELECTRIC_ENTRY, ///< Build an electric entry block signal
|
WID_BS_ELECTRIC_ENTRY, ///< Build an electric entry block signal
|
||||||
WID_BS_ELECTRIC_EXIT, ///< Build an electric exit block signal
|
WID_BS_ELECTRIC_EXIT, ///< Build an electric exit block signal
|
||||||
WID_BS_ELECTRIC_COMBO, ///< Build an electric combo block signal
|
WID_BS_ELECTRIC_COMBO, ///< Build an electric combo block signal
|
||||||
|
WID_BS_ELECTRIC_PROG, ///< Build an electric programmable signal
|
||||||
WID_BS_ELECTRIC_PBS, ///< Build an electric path signal.
|
WID_BS_ELECTRIC_PBS, ///< Build an electric path signal.
|
||||||
WID_BS_ELECTRIC_PBS_OWAY, ///< Build an electric one way path signal.
|
WID_BS_ELECTRIC_PBS_OWAY, ///< Build an electric one way path signal.
|
||||||
WID_BS_CONVERT, ///< Convert the signal.
|
WID_BS_CONVERT, ///< Convert the signal.
|
||||||
|
WID_BS_PROGRAM, ///< Enter program to prog signal
|
||||||
WID_BS_DRAG_SIGNALS_DENSITY_LABEL, ///< The current signal density.
|
WID_BS_DRAG_SIGNALS_DENSITY_LABEL, ///< The current signal density.
|
||||||
WID_BS_DRAG_SIGNALS_DENSITY_DECREASE, ///< Decrease the signal density.
|
WID_BS_DRAG_SIGNALS_DENSITY_DECREASE, ///< Decrease the signal density.
|
||||||
WID_BS_DRAG_SIGNALS_DENSITY_INCREASE, ///< Increase the signal density.
|
WID_BS_DRAG_SIGNALS_DENSITY_INCREASE, ///< Increase the signal density.
|
||||||
|
@@ -681,6 +681,11 @@ enum WindowClass {
|
|||||||
*/
|
*/
|
||||||
WC_SAVE_PRESET,
|
WC_SAVE_PRESET,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Programmable signals window
|
||||||
|
*/
|
||||||
|
WC_SIGNAL_PROGRAM,
|
||||||
|
|
||||||
WC_INVALID = 0xFFFF, ///< Invalid window.
|
WC_INVALID = 0xFFFF, ///< Invalid window.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user