diff --git a/Support/Release_Specs.py b/Support/Release_Specs.py index 31e0d4f..8b978e7 100644 --- a/Support/Release_Specs.py +++ b/Support/Release_Specs.py @@ -38,7 +38,7 @@ Release_Spec( root_path = project_dir / 'extensions/sn_debug_info', - steam = False, + steam = True, ), Release_Spec( @@ -82,7 +82,9 @@ 'documentation/Time_API.md':[ 'lua/time/Interface.lua', ], - # TODO: context menu. + 'documentation/Interact_Menu_API.md':[ + 'md/Interact_Menu_API.xml', + ], }, ), diff --git a/Support/steam_versions.json b/Support/steam_versions.json index 41a4aa4..2e82ef9 100644 --- a/Support/steam_versions.json +++ b/Support/steam_versions.json @@ -3,13 +3,14 @@ "sn_remove_dock_symbol": "1.2", "sn_station_kill_helper": "1.1", "sn_interact_collection": "1.1", - "sn_extra_game_options": "1.7", + "sn_extra_game_options": "1.8", "sn_better_target_monitor": "1.9", - "sn_mod_support_apis": "1.70", + "sn_mod_support_apis": "1.71", "sn_remove_dock_glow": "1.2", "sn_quiet_target_range_clicks": "1.1", "sn_remove_blinking_lights": "1.3", "sn_remove_dirty_glass": "1.2", "sn_remove_highway_blobs": "0.1", - "sn_start_with_seta": "1.0" + "sn_start_with_seta": "1.0", + "sn_debug_info": "1.1" } \ No newline at end of file diff --git a/extensions/extensions.pyproj b/extensions/extensions.pyproj index 21798ec..8f70217 100644 --- a/extensions/extensions.pyproj +++ b/extensions/extensions.pyproj @@ -95,8 +95,11 @@ + + + @@ -107,6 +110,7 @@ + diff --git a/extensions/link_to_extensions.bat b/extensions/link_to_extensions.bat index 8d2c9a8..ba6f0bc 100644 --- a/extensions/link_to_extensions.bat +++ b/extensions/link_to_extensions.bat @@ -21,14 +21,15 @@ for %%F in ( sn_station_kill_helper sn_debug_info + ) do ( mklink /J "%x4_path%\%%F" "%src_path%\%%F" ) +REM test_interact_menu_api +REM test_simple_menu_api REM test_hotkey_api -REM test_interact_menu_api REM test_named_pipes_api -REM test_simple_menu_api REM test_time_api REM test_sector_resize \ No newline at end of file diff --git a/extensions/sn_better_target_monitor/change_log.md b/extensions/sn_better_target_monitor/change_log.md index 4741cb8..c09c48e 100644 --- a/extensions/sn_better_target_monitor/change_log.md +++ b/extensions/sn_better_target_monitor/change_log.md @@ -30,7 +30,7 @@ Change Log - Lua file now exports the text table, for users wanting to monkey-patch in alternate ship identifiers. - German translation, by Le Leon. * 1.8 - - French translation update. + - French translation update, by Anthony. * 1.9 - Russian translation, by aladinaleks - Fixed content.xml text nodes. \ No newline at end of file diff --git a/extensions/sn_debug_info/change_log.md b/extensions/sn_debug_info/change_log.md new file mode 100644 index 0000000..c6236a5 --- /dev/null +++ b/extensions/sn_debug_info/change_log.md @@ -0,0 +1,7 @@ + +Change Log + +* 1.0 + - Start of log. +* 1.1 + - Added totals to station ware users. \ No newline at end of file diff --git a/extensions/sn_debug_info/content.xml b/extensions/sn_debug_info/content.xml index a6712f5..8a17f33 100644 --- a/extensions/sn_debug_info/content.xml +++ b/extensions/sn_debug_info/content.xml @@ -1,10 +1,10 @@ diff --git a/extensions/sn_debug_info/md/SN_Debug_Info.xml b/extensions/sn_debug_info/md/SN_Debug_Info.xml index 62f7625..018caad 100644 --- a/extensions/sn_debug_info/md/SN_Debug_Info.xml +++ b/extensions/sn_debug_info/md/SN_Debug_Info.xml @@ -723,6 +723,13 @@ + + + + + + @@ -731,6 +738,17 @@ + + + + + + + + + + + @@ -762,6 +780,7 @@ + @@ -769,16 +788,19 @@ + + + + - - + - - + + + + + + + + + + + + diff --git a/extensions/sn_extra_game_options/Readme.md b/extensions/sn_extra_game_options/Readme.md index 67af41f..13cdc81 100644 --- a/extensions/sn_extra_game_options/Readme.md +++ b/extensions/sn_extra_game_options/Readme.md @@ -9,3 +9,7 @@ Adds several new game options, accessible through the Extension Options page of ### Requirements * Simple_Menu_API + +### Contributors +* Anthony Bouquet - French translation +* Le Leon - German translation \ No newline at end of file diff --git a/extensions/sn_extra_game_options/change_log.md b/extensions/sn_extra_game_options/change_log.md index 2bcf5f9..586a1bb 100644 --- a/extensions/sn_extra_game_options/change_log.md +++ b/extensions/sn_extra_game_options/change_log.md @@ -17,6 +17,10 @@ Change Log - Prevented experimental FoV slider from having unwanted effects. * 1.6 - Fixed content.xml text nodes. + - French translation, by Anthony. * 1.7 - Added mass traffic density option. - - Fixed auto mousover text losing some formatting (eg. underlines). \ No newline at end of file + - Fixed auto mousover text losing some formatting (eg. underlines). +* 1.8 + - Adjusted help text suppression to work during a pause. + - German translation, by Le Leon. \ No newline at end of file diff --git a/extensions/sn_extra_game_options/content.xml b/extensions/sn_extra_game_options/content.xml index e3659b3..091d441 100644 --- a/extensions/sn_extra_game_options/content.xml +++ b/extensions/sn_extra_game_options/content.xml @@ -4,7 +4,7 @@ name="Extra Game Options" description=" " author="SirNukes" - version="107" + version="108" date="2020-3-12" save="false" sync="false"> diff --git a/extensions/sn_extra_game_options/lua/Custom_Options.lua b/extensions/sn_extra_game_options/lua/Custom_Options.lua index 42f6628..4a436c0 100644 --- a/extensions/sn_extra_game_options/lua/Custom_Options.lua +++ b/extensions/sn_extra_game_options/lua/Custom_Options.lua @@ -15,7 +15,7 @@ blackboard table instead of using signal params (limited to string/int/nil). TODO: possible future options - Reduce config.startAnimation.duration for faster open animations. - Increase ui scaling factor beyond 1.5 (needs monkeypatch). -- Remove modified tag entirely. +- Remove modified tag entirely (text and parenthesis). Requires accessing gameoptions config.optionDefinitions (private), or monkeypatching wherever it gets used with a custom copy/pasted version with the modified text function removed. @@ -29,9 +29,7 @@ TODO: possible future options a debug menu. - Try out some global functions: - GetTrafficDensityOption / SetTrafficDensityOption GetCharacterDensityOption - ClearLogbook - Remove the egosoft station announcement On page {10099,1014}. @@ -45,11 +43,10 @@ TODO: possible future options Note: most interesting stuff is in ui/core, but those don't see to be exported despite being globals. -- Edit helptext.lua to suppress display of help messages. Should be simple - to intercept onShowHelp and onShowHelpMulti calls with doing nothing. - Main problem is that testing the edit would be annoying; need to find - a situation that consistently pops up the text. Probably will be - correct on first try, though. +- Higher ui scaling values + menu.valueGameUIScale, menu.callbackGameUIScaleReset() + Normally limited to 1.5x. + Maybe of little use; ui is already problematic at 1.5 with text cuttoffs. ]] @@ -498,18 +495,19 @@ end -- This gets checked on each onUpdate, looking for a time change. L.helptext.delay_func = function() - if L.helptext.start_time ~= GetCurTime() then + if L.helptext.start_time ~= GetCurRealTime() then L.helptext.clear_text_func() end end -- Set up the onUpdate script, and record the start time. +-- TODO: switch to Time api for 1 frame delay alarm. L.helptext.setup_func = function() -- Note: several helptext calls may be done at once, seemingly, -- so prevent excess calls to SetScript (clean up log). if L.helptext.polling_active == false then L.helptext.polling_active = true - L.helptext.start_time = GetCurTime() + L.helptext.start_time = GetCurRealTime() SetScript("onUpdate", L.helptext.delay_func) end end diff --git a/extensions/sn_interact_collection/md/SN_Interact_Commands.xml b/extensions/sn_interact_collection/md/SN_Interact_Commands.xml index 6292c7f..2b69c56 100644 --- a/extensions/sn_interact_collection/md/SN_Interact_Commands.xml +++ b/extensions/sn_interact_collection/md/SN_Interact_Commands.xml @@ -15,25 +15,34 @@ - - + - + - - - + + + + + + + + + + + @@ -52,7 +61,7 @@ - + diff --git a/extensions/sn_mod_support_apis/Readme.md b/extensions/sn_mod_support_apis/Readme.md index 308e887..ee4f00b 100644 --- a/extensions/sn_mod_support_apis/Readme.md +++ b/extensions/sn_mod_support_apis/Readme.md @@ -73,12 +73,6 @@ Two types of menus are supported: options menus that are integrated with the sta Additionally, a simplified interface is provided for adding options to an extension. These options are simple buttons and sliders that display in the main Extension Options menu. - -### Requirements - -* Lua Loader API extension - - https://github.com/bvbohnen/x4-lua-loader-api.git - ### Usage * Basic extension options @@ -184,26 +178,31 @@ Additionally, a simplified interface is provided for adding options to an extens # X4 Interact Menu API This extension implements a generic mission-director level api for adding custom interact menu actions (eg. the right-click menu). A lua backend interfaces with the egosoft menu code; api users may work purely with mission director scripts. -Warning: this api is in an early version. The method of selecting when an action is valid, and the format of the object returned to the callback cue, are likely to change. +When a menu is first opened, information is gathered from lua and passed to the md.Interact_Menu_API.Get_Actions cue. Users may listen to this cue, check the menu parameters, and on wanted conditions add a new action using Add_Action, which defines a callback cue if the player selects the action. ### Example Usage This code adds a generic "Follow" action to any target. ```xml - + - + - + + + + @@ -231,17 +230,15 @@ There are three components to this API: ### Requirements -* Optionally, Python 3.6+ with the pywin32 package. - - Only needed if running the pipe server from python source code. +* Windows + - Currently, pipes are only set for Windows, not Linux. +* The X4_Python_Pipe_Server (exe or python source code version). + - Run this pipe server alongside X4. +* Optionally, Python 3.6+ with the pywin32 package if running from source. + - Only needed if not using the standalone exe. - An executable is provided as an alternative. - The pywin32 package is part of the Anaconda distribution of python by default. -### Installation - -* Place the named_pipes_api folder in extensions. -* Place the X4_Python_Pipe_Server (exe or py) anywhere convenient. - - Run this pipe server alongside X4. - ### Components * MD Pipe API @@ -274,8 +271,8 @@ An external Python server is used for the key capture and combo recognition, and ### Requirements -* Optionally, if using the python pipe server, the pywin32 and pynput packages. - - Not needed when using the prebuilt server executable. +* If running the python pipe server from source, the pywin32 and pynput packages. + - Not needed when using the server exe (which includes these packages). ### Usage @@ -312,34 +309,22 @@ Example direct key mapping: Limitations: * The windows backend confuses numpad enter and numpad / with their non-numpad counterparts. If one of these keys is used, both the numpad and normal key will trigger the hotkey callback. -* Currently only supports keyboard inputs. - -See "API Functions.md" for full details. - - +* Currently only supports keyboard inputs, not mouse or joystick. # X4 Time API -This api provides additional real-time timing functions to mission director scripts. These timers will continue to run while the game is paused. +This api provides additional timing functions to mission director scripts and lua modules. These timers will continue to run while the game is paused. Timing is done using two sources: * The lua function GetCurRealTime(), which measures the seconds since X4 booted up, advancing each frame while X4 is active (eg. not while minimized). -* Python 'time' module, accessed through the Named Pipes API, providing sub-frame timing. +* Optionally, the python 'time' module, accessed through the Named Pipes API, which can track time changes while the game is paused and minimized. Example uses: - In-menu delays, eg. a blinking cursor. - Timing for communication through a pipe with an external process. - Code profiling. - -### Requirements - -* Optionally, Named Pipes API extension - - https://github.com/bvbohnen/x4-named-pipes-api - - Needed for the commands that utilize external python timing. - - ### Usage General commands are sent using raise_lua_event of the form "Time.command". @@ -348,6 +333,8 @@ each command will take an [id] unique string parameter. Responses (if any) are captured using event_ui_triggered with screen "Time" and control [id]. Return values will be in "event.param3". +Note: the X4 engine processes MD scripts earlier in a frame than lua scripts. Any responses from lua back to md will have a 1 frame delay. + Warning: when a game is saved and reloaded, all active timers will be destroyed and any pending alarms will not go off. Standard Commands: @@ -380,7 +367,7 @@ Standard Commands: creating clocks or similar. - Note: precision based on game framerate. -High precision commands (require Named Pipes API and running host server): +External timer commands (requires a running named pipes host server): - getSystemTime ([id]) - Returns the system time reported by python through a pipe. @@ -393,6 +380,7 @@ High precision commands (require Named Pipes API and running host server): - Stops the timer associated with tic, returns the time measured, and prints the time to the debug log. +Additional lua commands are documented in the time interface.lua file. * Example: get engine time. ```xml @@ -414,7 +402,7 @@ High precision commands (require Named Pipes API and running host server): ``` -- Example: set an alarm. +- Example: set an alarm (works while paused). ```xml diff --git a/extensions/sn_mod_support_apis/change_log.md b/extensions/sn_mod_support_apis/change_log.md index 5a53c43..fbf2b1a 100644 --- a/extensions/sn_mod_support_apis/change_log.md +++ b/extensions/sn_mod_support_apis/change_log.md @@ -19,4 +19,6 @@ Change Log for overall api package. - Added Write_Option_Value and Read_Option_Value cues. - simple_menu: - Fixed column indexing for Call_Table_Method on options menus. - - Added options menu onOpen signal param with $id, $echo, $columns. \ No newline at end of file + - Added options menu onOpen signal param with $id, $echo, $columns. +* 1.71 + - Large expansion of the Interact Menu API, including dynamic condition checking in md to determine which actions should show, better control over action display (icons, left/right text, etc), and providing much more information on the context in which a menu is opened. \ No newline at end of file diff --git a/extensions/sn_mod_support_apis/content.xml b/extensions/sn_mod_support_apis/content.xml index 5336a89..55315e6 100644 --- a/extensions/sn_mod_support_apis/content.xml +++ b/extensions/sn_mod_support_apis/content.xml @@ -4,7 +4,7 @@ name="SirNukes Mod Support APIs" description=" " author="SirNukes" - version="170" + version="171" date="2019-07-23" save="false" sync="false"> diff --git a/extensions/sn_mod_support_apis/documentation/Interact_Menu_API.md b/extensions/sn_mod_support_apis/documentation/Interact_Menu_API.md new file mode 100644 index 0000000..75f7f6c --- /dev/null +++ b/extensions/sn_mod_support_apis/documentation/Interact_Menu_API.md @@ -0,0 +1,399 @@ + +### MD Interact Menu API Overview + + +MD API support for working with interaction menus (eg. right-click context menus). Listen for Get_Actions being signalled when a menu opens, check conditions and add relevant actions with Add_Actions, wait for callbacks if a player selects a custom action. + + +### MD Interact Menu API Cues + +* **Get_Actions** + + Cue used to signal when a new menu is being opened, and actions may be added. + + The cue event.param holds a table with target data: + * $object + - The object the action was selected for, eg. a ship. + * $texts + - Table with several text strings used in context menus. + - Possible fields are described further below, in the Texts section. + * + - Other menu parameters are included and described further below, in the Params section. + +* **Add_Action** + + + Add an action to a newly created interact menu. This should be called just following a menu opening event, signalled from lua, holding info on the target object. These actions are removed from the menu after it closes, and need to be re-added on the next menu opening. + + This should be called whenever the API signals md.Interact_Menu_API.Get_Actions with target data. + + Input to this cue is a table with the following fields: + * $id + - String, unique identifier for this action. + * $text + - String, text to display in the action menu, left column. + * $icon + - String, optional name of an icon to prefix before the text. + * $text2 + - String, optional text to display in the right column. + - Support for this varies with $section. Eg. 'main' supports text2 while 'interaction' does not. + * $mouseover + - String, optional text to display on menu widget mouseover. + * $mouseover_icon + - String, optional name of an icon to prefix before the mouseover text. + * $section = 'main' + - Optional string, the menu section this action will go under. + - Should be one from menu_interactmenu.lua config.sections. + - TODO: Document these somewhat. + - For now, just use "main" or "interaction". + * $callback + - Cue to call when the player selects the action. + - See below for event.param contents. + * $keep_open + - Bool, if the menu should be left open after this action is selected. + - Defaults false, closing the menu. + * $active + - Bool, if false then the action will be greyed out and unselectable. + - Defaults true. + * $echo + - Optional, anything (string, value, table, etc.), data to be attached to the callback cue param for convenience. + + + The callback cue returns an event.param table with the following: + * $id + - Same as $id above. + * $echo + - Same as $echo above. + * $object + - The object the action was selected for, eg. a ship. + - Possibly null. + - This is the same as in Get_Actions. + * [params] + - Other menu parameters are included and described further below, in the Params section. + - These are the same as in Get_Actions. + * $texts + - Table with several text strings used in context menus. + - Possible fields are described further below, in the Texts section. + - These are the same as in Get_Actions. + + Example: + ```xml + + + + + + + + + + + + ``` + +* **Update_Action** + + + Updates fields of a currently recorded action. Note: currently this will not update a displayed menu's actions, since those are determined when the menu is first drawn. + + Input to this cue is a table with the following fields: + * $id + - String, unique identifier matching an existing action. + * [params] + - Other params should match existing ones, and will overwrite them. + + Example: + ```xml + + ``` + +* **Reloaded** + + Dummy cue used for signalling that this api reloaded. Users that are registering options should listen to this cue being signalled. Somewhat depricated in favor of Get_Actions. + +* **Register_Action** + + + Register a new context menu action. If the action already exists, it will be updated with the new arguments. These actions are persistent, and will be checked every time the menu options for condition matches. + + Note: slightly depricated in favor of Add_Action. + + This should be called whenever the API signals md.Interact_Menu_API.Reloaded + + Input is a table with the following fields: + * $id + - String, unique identifier for this action. + * $text + - String, text to display in the action menu. + * $icon + - String, optional name of an icon to prefix before the name. + - Typical order icons are 32x32, though any icon given will be scaled to 32 height. + * $section = 'main' + - Optional string, the menu section this action will go under. + - Should be one from menu_interactmenu.lua config.sections. + - TODO: Document these somewhat. + - For now, just use "main" or "interaction". + * $enabled_conditions + - List of strings, flag names understood by the backend, of which at least one must be True to enable the action. + * $disabled_conditions + - List of strings, flag names understood by the backend, of which all must be False to enable the action. + * $mouseover + - String, text to display on menu widget mouseover. + * $callback + - Cue to call when the player selects the action. + - See below for event.param contents. + * $echo + - Optional, anything (string, value, table, etc.), data to be attached to the callback cue param for convenience. + * $disabled = 0 + - Optional, 0 or 1; if the option will not be displayed in the menu. + + + The callback cue returns an event.param table with the following: + * $id + - Same as $id above. + * $echo + - Same as $echo above. + * $object + - The object the action was selected for, eg. a ship. + + + The flags available for matching include the following. All are strings, and may be negated by a prefixed '~', eg. '~isenemy'. + * Component class + - class_controllable + - class_destructible + - class_gate + - class_ship + - class_station + * Component data + - is_dock + - is_deployable + - is_enemy + - is_playerowned + * Menu flags + - show_PlayerInteractions + - Menu flagged to show player interactions. + - has_PlayerShipPilot + - Selection is a player ship and has a pilot. + * Misc + - is_operational + - Selection is operational? + - is_inplayersquad + - Selection is in the player's squad. + - has_pilot + - Selection has a pilot. + - have_selectedplayerships + - Selection(s) include one or more player ships. + * Player related + - player_is_piloting + - True if the player is piloting a ship. + - is_playeroccupiedship + - Selection is the player's ship. + + + Example: + ```xml + + + + + + + + + ``` + + + + +#### Params + +When an interact menu is opened, various parameters on the target object and the source object(s) are populated, and used to guide which actions will show and what to do when actions are taken. A version of these params will be polished for MD usage, and passed to the event.param of Get_Actions and any action callback cues. + +The possible params are as follows. Not all of these will exist for every target type. + +* $object + - Target object, or possibly a parent of the target. + - When selecting a spot on a map, may be a sector. + - May be null, eg. when opening a context menu for a mission. +* $isshipconsole + - Bool, True if the target is a ship console. + - This includes when the player selects the console at a docking pad. +* $isdockedship + - Bool, True if a ship console is open at a dock with a docked ship. + - If True, the $object will be the docked ship. + - If False for a ship console, it indicates the console is for an empty dock, and $object is that dock. +* $selectedplayerships + - List of player ships that are currently selected. + - This is often populated by default with the player-piloted ship if the $object isn't the player ship. +* $showPlayerInteractions + - Bool, True if the menu wants to show player interactions with the object. + - Convenience term that gets set when $selectedplayerships is a list with only the player occupied ship in it. + - Typically true when the player opens an interact menu on another object while flying. +* $hasPlayerShipPilot + - Bool, True if a ship in $selectedplayerships has an assigned pilot and is not the player occupied ship. + - This will always be False if $showPlayerInteractions is True. +* $selectedplayerdeployables + - List of player deployables that are currently selected. +* $selectedotherobjects + - List of other objects that are currently selected, eg. ships and stations. +* $order_queueidx + - Int, index of an order in the queue, if target is an order. + - May be unspecified. +* $subordinategroup + - Int, 1-24, matches the corresponding greek letter of a selected subordinate group. + - May be unspecified. +* $construction + - Object under construction, which the menu opened on. + - This occurs in the map view, looking at a shipyard, right clicking on a ship under construction, in which case the $object is the shipyard and $construction is the ship. + - May be unspecified. +* $mission + - ID of an active mission. + - May be unspecified. +* $missionoffer + - ID of a mission offer. + - May be unspecified. +* $componentMissions + - Potentially a list of mission ids (untested). + - Not verified in game as ever being populated, though the table will exist when opening a menu for a station from the map. + - May be unspecified. +* $offsetcomponent + - Reference object for a position targeted, often a sector. + - May be unspecified. +* $offset + - Position offset from $offsetcomponent of a target. + - May be unspecified. + + + + +#### Texts + +In lua, various potentially useful text strings are created based on the target and selected objects. They are passed over to md in the Get_Actions event.param, and listed here. Note: many of these fields may not exist for a given target. + +* $targetShortName + - Name of the target. + - This should always be present. + - Missions and mission offers will lack any other text. +* $targetName + - Name of the target with color prefix, object id, and other fields as applicable (eg. gate destination). +* $targetBaseName + - Ships only, the short base ship name. +* $targetBaseOrShortName + - Either $targetBaseName if defined, else $targetShortName. + - This should always be present. + - Vanilla actions often use $targetBaseName if available, else $targetShortName, as text2; this is a convenience term added to mimic that behavior. +* $commanderShortName + - Objects with commanders only, commander name. +* $commanderName + - Objects with commanders only, command name with sector prefix and if, as applicable. +* $selectedName + - If player ships selected, the name of the ship (if one) or an indicator of number of ships selected. +* $selectedFullNames + - If player ships selected, names of all ships separated by newlines, suitable for mouseover. +* $selectedNameAll + - If object is player owned ship, the count of selected ships including the menu target. +* $selectedFullNamesAll + - As $selectedFullNames, but including the target object. +* $otherName + - As $selectedName, but for selected other objects (not ships). +* $otherFullNames + - As $selectedFullNames, but for selected other objects (not ships). +* $constructionName + - Construction only, name of the construction. +* $buildstorageName + - Build storage only, name of the build storage. + + + + + +#### Sections and subsections + +The following is a quick listing of the different context menu sections and subsections an action can be added to. Actions in a subsection will show in the expanded menu on mouseover. + +* main +* interaction +* hiringbuilderoption + - hiringbuilder +* trade +* playersquad_orders +* main_orders +* formationshapeoption + - formationshape +* main_assignments +* main_assignments_subsections + - main_assignments_defence + - main_assignments_attack + - main_assignments_interception + - main_assignments_supplyfleet + - main_assignments_mining + - main_assignments_trade + - main_assignments_tradeforbuildstorage +* order +* guidance +* player_interaction +* consumables + - consumables_civilian + - consumables_military +* cheats +* selected_orders_all +* selected_orders +* mining_orders + - mining +* venturedockoption + - venturedock +* trade_orders +* selected_assignments_all +* selected_assignments + - selected_assignments_defence + - selected_assignments_attack + - selected_assignments_interception + - selected_assignments_supplyfleet + - selected_assignments_mining + - selected_assignments_trade + - selected_assignments_tradeforbuildstorage +* selected_consumables + - selected_consumables_civilian + - selected_consumables_military +* shipconsole + +Sections have a couple special properties, which relate to when a section's actions will be shown. They are listed here, to better indicate when each section will be shown. + +* isorder + - Relates to if a section is shown when player ships are selected. + - true: + - selected_orders_all, selected_orders, mining_orders, venturedockoption, trade_orders, selected_assignments_all, selected_assignments, selected_consumables + - false: + - main, interaction, hiringbuilderoption, trade, playersquad_orders, main_orders, formationshapeoption, main_assignments, main_assignments_subsections, player_interaction, consumables, cheats, shipconsole + - undefined: + - order, guidance +* isplayerinteraction + - Shown when a single player-owned ship is selected, and the player occupies it. + - true: + - guidance, player_interaction + - undefined: + - all other categories + + \ No newline at end of file diff --git a/extensions/sn_mod_support_apis/documentation/Simple_Menu_API.md b/extensions/sn_mod_support_apis/documentation/Simple_Menu_API.md index 6dd3c3d..2f00411 100644 --- a/extensions/sn_mod_support_apis/documentation/Simple_Menu_API.md +++ b/extensions/sn_mod_support_apis/documentation/Simple_Menu_API.md @@ -264,7 +264,7 @@ Complex properties: * backgroundColor = 'Helper.color.white' - Color of the background texture. - onCloseElement event event returns: + onCloseElement event returns: * echo, event, id * reason - String, reason for the closure. diff --git a/extensions/sn_mod_support_apis/documentation/Simple_Menu_Options_API.md b/extensions/sn_mod_support_apis/documentation/Simple_Menu_Options_API.md index 58dcb6f..5a8c7fd 100644 --- a/extensions/sn_mod_support_apis/documentation/Simple_Menu_Options_API.md +++ b/extensions/sn_mod_support_apis/documentation/Simple_Menu_Options_API.md @@ -18,7 +18,7 @@ - Optional string, category name under which this option will be placed, along with any others of the same category. - If not given, or given as "General" will be set to General. * $mouseover - - String, text to display on menu widget mouseover. + - Optional string, text to display on menu widget mouseover. * $type - String, name of the type of widget to create. - "button": an on/off button. diff --git a/extensions/sn_mod_support_apis/documentation/Time_API.md b/extensions/sn_mod_support_apis/documentation/Time_API.md index 324d2e6..70ee2a3 100644 --- a/extensions/sn_mod_support_apis/documentation/Time_API.md +++ b/extensions/sn_mod_support_apis/documentation/Time_API.md @@ -17,8 +17,10 @@ Other lua modules may require() this module to access these api functions: * Set_Alarm(id, time, function) - Sets a single-fire alarm to trigger after the given time elapses. - Callback function is called with args: (id, alarm_time), where the alarm_time is the original scheduled time of the alarm, which will generally be sometime earlier than the current time (due to frame boundaries). +* Set_Frame_Alarm(id, frames, function) + - As above, but measures time in frame switches. -An MD ui event is raised on every frame, which MD cues may listen to. The event.param3 will be the current engine time. Example: `` +An MD ui event is raised on every frame, which MD cues may listen to. This differs from a cue firing every 1ms in that this works when paused. The event.param3 will be the current engine time. Example: `` MD commands are sent using raise_lua_event of the form "Time.", and responses (if any) are captured in screen "Time" with control "id". Return values will be in "event.param3". Note: since multiple users may be accessing the timer during the same period, each command will take an id unique string parameter. diff --git a/extensions/sn_mod_support_apis/lua/interact_menu/Interface.lua b/extensions/sn_mod_support_apis/lua/interact_menu/Interface.lua index c5f15c9..284b671 100644 --- a/extensions/sn_mod_support_apis/lua/interact_menu/Interface.lua +++ b/extensions/sn_mod_support_apis/lua/interact_menu/Interface.lua @@ -1,30 +1,58 @@ --[[ Module for adding new context menu actions. -Note: not dependend on the simple menu flow directly, except for +Note: not dependent on the simple menu flow directly, except for some library functions. -TODO: maybe split off into separate extension. +menu.showInteractMenu() +- Sets up the component, or selected objects. +- Fills menu state data from call params (processed elsewhere?). +- Calls menu.display() + +menu.onShowMenu() +- Also sets up the component, or selected objects. +- Has more logic for setting up menu state data, eg. filling a default + selectedplayerships. +- Calls menu.display() + +menu.display() +- Records mouse position (not use anywhere?) +- Calls menu.prepareActions() +- Calls menu.draw() +- Can potentially intercept and delay this one frame to get md responses. +- Note: don't just delay this by a frame, since an onUpdate can fire before + display() is called, causing problems. +- Could possibly delay this while suppressing onUpdate. + +menu.draw() +- Sets up widget stuff. +- Records menu.mouseOutBox. + +menu.onUpdate() +- Checks menu.mouseOutBox. +- Calls menu.prepareActions() if player activity changed (eg. a scan completed) +- Calls menu.draw() if things have changed. +- Note: to suppress onUpdate, note that when the menu showMenuCallback kicks + off in Helper, onUpdate is recorded into a wrapper function, so an extra + wrapper will need to be created here and manipulated for its internal + call to menu.onUpdate. Further, Helper.interactMenuCallbacks.update is + another link to menu.onUpdate, which also needs switching. -menu.display() calls menu.prepareActions() - -menu.onUpdate() calls menu.prepareActions() if it thinks things have -changed (eg. a scan completed) - -prepareActions() +menu.prepareActions() - Bunch of logic for different possible actions. - Standard action list obtained externally from: - C.GetCompSlotPlayerActions(buf, n, menu.componentSlot) - Action list depends on target component? - Makes many conditional calls to insertInteractionContent() to register the actions. +- Can be followed with adding new actions. insertInteractionContent(section, entry) Appears to register a new action. * section - String, matching something in config.sections - Observed: + String, matching a section name in config.sections + Eg: main player_interaction selected_orders @@ -34,10 +62,18 @@ insertInteractionContent(section, entry) Table with some subfields. * type - String, often called actiontype, eg. "teleport", "upgrade", etc. - - Doesn't seem to have other uses in the lua, but was for the - C labelling to lua to read. + - When loading predefined orders from C ffi, type names are used + to select which section to use. + - Gets set to the widget uiTriggerID, which will cause a ui + event of this name when the widget is clicked. + - Can leave as nil to prevent excess ui events, particularly to + avoid an accidental collision to existing ui listeners. * text - String, display text + - Icons appear to be added using a special text term: + - "\27[]" + * text2 + - String, right side text? * helpOverlayID - String, unique gui id? Never seems to be used. * helpOverlayText @@ -49,10 +85,64 @@ insertInteractionContent(section, entry) - Often given as a "menu.button..." function in this lue module. * hidetarget - Bool, often true + - TODO: what is this? useful? * active - Lua function, returns bool? + - Greys it out if unnactive, maybe? + - No smooth and easy way to hook into this dynamically from md. * mouseOverText - String + +Notes on subsections: + Sections are defined in the local config table, and sometimes have + subsections. + Each action is assigned a section, recorded into menu.actions[section] lists. + Subsections are shown if they have actions recorded, or are forced to + show by being added to a "menu.forced[section] = name" table of names. + + Since config is static, there is no good way to create new subsections. + New actions should use existing sections. + +Some menu state data of interest: +* componentSlot.component + - Raw component UniverseID, the target of the menu. +* connection, componentSlot.connection + - Some string?; not used anywhere obvious. +* selectedplayerships + - List of player owned ships selected, explicit or implicit. + - Converted components (ConvertStringTo64Bit out). + - When empty for onShowMenu, fills with the player's ship (piloted or + occupying). +* selectedplayerdeployables, selectedotherobjects + - Lists + - Converted components +* mode + - Either nil or "shipconsole". + - Only set up by onShowMenu. +* isdockedship + - Either nil or true/false. + - Only set by onShowMenu when mode == "shipconsole" +* construction + - Construction object. Has attributes: inprogress, component, macro, id. + - Cancel action operates on id against the componentSlot.component. +* mission + - ID of a mission already active. +* missionoffer + - ID of a mission offer. +* componentOrder + - Order object, generally using a menu.componentOrder.queueidx value. +* subordinategroup + - Appears to be an integer 1-24 matching a wing number. + - Used directly look up text entry on page 20401 (greek letters). +* offsetcomponent + - UniverseID Sector (or something else), a reference position in space? +* offset + - UIPosRot, relative offset from offsetcomponent. +* componentMissions + - List of mission ids. +* playerSquad + - Just a list populated with subordinates of the player ship. + - MD can recreate this if needed. ]] -- Set up any used ffi functions. @@ -63,7 +153,31 @@ ffi.cdef[[ bool IsUnit(UniverseID controllableid); UniverseID GetPlayerOccupiedShipID(void); UniverseID GetPlayerControlledShipID(void); - UniverseID GetPlayerContainerID(void); + UniverseID GetPlayerContainerID(void); + typedef struct { + const char* missionName; + const char* missionDescription; + int difficulty; + int upkeepalertlevel; + const char* threadType; + const char* mainType; + const char* subType; + const char* subTypeName; + const char* faction; + int64_t reward; + const char* rewardText; + size_t numBriefingObjectives; + int activeBriefingStep; + const char* opposingFaction; + const char* license; + float timeLeft; + double duration; + bool abortable; + bool hasObjective; + UniverseID associatedComponent; + UniverseID threadMissionID; + } MissionDetails; + MissionDetails GetMissionIDDetails(uint64_t missionid); ]] -- Use local debug flags. @@ -72,19 +186,36 @@ local debugger = { verbose = false, } local Lib = require("extensions.sn_mod_support_apis.lua.Library") +local Time = require("extensions.sn_mod_support_apis.lua.time.Interface") -- Table of locals. local L = { + settings = { + -- Extension option setting to disable these hooks, for safety. + disabled = false, + }, + -- Component of the player. player_id = nil, + -- Flag indicating if a menu display is currently being delayed + -- by a frame. + delaying_menu = false, + -- List of queued command args. queued_args = {}, -- Table of custom actions, keyed by id. - actions = {}, + -- Old style: perpetual actions loaded at startup. + static_actions = {}, + -- New style: one-use actions sent on menu open. + temp_actions = {}, + + -- Lists of ids, order in which action ids were declared. + static_actions_order = {}, + temp_actions_order = {}, -- Current table of target object flags. flags = nil, @@ -107,6 +238,8 @@ function L.Init() L.Init_Patch_Menu() end + +-- Extract blackboard args. function L.Get_Next_Args() -- If the list of queued args is empty, grab more from md. @@ -130,7 +263,7 @@ function L.Get_Next_Args() return args end - +-- Process a command sent from md. function L.Handle_Process_Command(_, param) local args = L.Get_Next_Args() @@ -139,10 +272,52 @@ function L.Handle_Process_Command(_, param) end -- Process command. + -- Old version, perpetual action with flag checks. if args.command == "Register_Action" then - L.actions[args.id] = args + -- If not seen yet, record the id ordering. + if L.static_actions[args.id] == nil then + table.insert(L.static_actions_order, args.id) + end + L.static_actions[args.id] = args + + -- New version, single-use action. + elseif args.command == "Add_Action" then + -- If not seen yet, record the id ordering. + if L.temp_actions[args.id] == nil then + table.insert(L.temp_actions_order, args.id) + end + L.temp_actions[args.id] = args + + -- Update fields of an existing action. + elseif args.command == "Update_Action" then + + -- Check temp and static actions. + local action = nil + if L.temp_actions[args.id] ~= nil then + action = L.temp_actions[args.id] + elseif L.static_actions[args.id] ~= nil then + action = L.static_actions[args.id] + else + DebugError("Interact API: Update_Action has unmatched id: "..tostring(args.id)) + end + + -- Copy over fields. + for field, value in pairs(args) do + -- Skip the id (though should be harmless to modify). + if field ~= "id" then + action[field] = value + end + end + + -- Change any settings. If name doesn't match a setting, it will + -- be recorded but just not used. + elseif args.command == "Update_Settings" then + -- Convert 0 to false. + local value = args.value + if value == 0 then value = false end + L.settings[args.setting] = value else - DebugError("Unrecognized command: "..tostring(args.command)) + DebugError("Interact API: Unrecognized command: "..tostring(args.command)) end end @@ -153,22 +328,183 @@ function L.Init_Patch_Menu() -- Look up the menu, store in this module's local. menu = Lib.Get_Egosoft_Menu("InteractMenu") - -- Patch the action prep. + + -- Wrap onUpdate, so it can be suppressed. Note that Helper will link + -- to this when the menu showMenuCallback kicks off, which is fine + -- with this style of wrapping. (Temporary wraps wouldn't work so well.) + -- Note: onUpdate will get called 1-2 times before the frame delay + -- completes, due to event order alignment (and that onUpdate fires + -- on the same frame the menu display() was called, needlessly). + local ego_onUpdate = menu.onUpdate + menu.onUpdate = function(...) + if not L.delaying_menu then + ego_onUpdate(...) + end + end + -- Also update Helpers dedicated link to the interactmenu properties. + Helper.interactMenuCallbacks.update = menu.onUpdate + + -- Do the same for a couple others. + -- Note: these could just be suppressed to a select display() call, + -- but doing it this way is a little easier to recover on an error + -- since it doesn't risk leaving the menu attached to dummy functions. + local ego_draw = menu.draw + menu.draw = function(...) + if not L.delaying_menu then + ego_draw(...) + end + end + + -- Patch prepareActions to slot in the custom function afterward. local ego_prepareActions = menu.prepareActions menu.prepareActions = function (...) - -- Run the standard setup first. - ego_prepareActions(...) + if not L.delaying_menu then + -- Run the standard setup first. + ego_prepareActions(...) + + -- Safety call to add in the new actions. + if not L.settings.disabled then + local success, error = pcall(L.Add_Actions, ...) + if not success then + DebugError("Interact API prepareActions error: "..tostring(error)) + end + end + end + end + + -- Patch the initial display. + local ego_display = menu.display + menu.display = function(...) - -- Safety call to add in the new actions. - local success, error = pcall(L.Add_Actions, ...) - if not success then - DebugError("prepareActions error: "..tostring(error)) + -- If disabled in options (as a safety against breaking), just pass + -- through the call. + if L.settings.disabled then + -- Ensure this flag is false, to be safe. + L.delaying_menu = false + ego_display(...) + + else + -- Flag that display is active, to suppress some functions. + L.delaying_menu = true + + -- Run the display function; prepareActions and draw will + -- be suppressed. This will basically record the mouse position + -- and fill in texts. + ego_display(...) + + -- Request actions from md; this reads texts from above. + local success, error = pcall(L.Signal_MD_Get_Actions) + if not success then + DebugError("Interact API error in Signal_MD_Get_Actions: "..tostring(error)) + end + + -- Delay one frame, so md can respond. + Time.Set_Frame_Alarm('options_menu_delay', 1, L.Delayed_Draw) end end + +end + +-- 1-frame delayed menu draw. +function L.Delayed_Draw() + -- Stop suppressing some functions. + L.delaying_menu = false + + -- Run the suppressed functions. + menu.prepareActions() + menu.draw() +end + +-- Function called when a menu is first opened. +-- Tells md to load actions for the target. +function L.Signal_MD_Get_Actions() + local ret_table = { + + -- The specific object selected. + -- Note: maybe be null (0); led md deal with that case. + object = ConvertStringTo64Bit(tostring(menu.componentSlot.component)), + + -- If this is a ship console. + isshipconsole = menu.mode == "shipconsole", + isdockedship = menu.isdockedship == true, + + -- Ensure these have false values if nil. + showPlayerInteractions = menu.showPlayerInteractions or false, + hasPlayerShipPilot = menu.hasPlayerShipPilot or false, + + -- Multiple player ships could be selected, presumably + -- including the above component. + -- This list appears to already be convertedComponents, + -- so can return directly. + selectedplayerships = menu.selectedplayerships, + selectedplayerdeployables = menu.selectedplayerdeployables, + selectedotherobjects = menu.selectedotherobjects, + + -- Various text snippets might be useful. + texts = menu.texts, + + -- These components are polished further below. + offsetcomponent = menu.offsetcomponent, + -- For construtions, try just passing the component itself? TODO + construction = menu.construction and menu.construction.component, + + -- Only the order queueidx (or nil). + order_queueidx = menu.componentOrder and menu.componentOrder.queueidx, + + -- Int 1-24 (or nil) greek letter index. + subordinategroup = menu.subordinategroup, + + -- Just pass mission ids through as-is; attempts to convert them + -- to something like parent cues didn't work out below. + mission = menu.mission, + missionoffer = menu.missionoffer, + componentMissions = menu.componentMissions, + + -- Manually convert positions (expand here, repack in md). + offset = menu.offset and { + x = menu.offset.x, y = menu.offset.y, z = menu.offset.z}, + } + -- -Removed; made no difference to the mission id value. + ---- Missions are a bit odd, giving a mission id, but MD-side missions + ---- are attached to cues and always accessed through those cues. + --for _, field in ipairs({"mission","missionoffer"}) do + -- if menu[field] then + -- -- Makes no difference? + -- local missiondetails = C.GetMissionIDDetails(menu[field]) + -- ret_table[field] = missiondetails.associatedComponent + -- end + --end + + -- Convert some components, if they exist. + for _, field in ipairs({"offsetcomponent","construction"}) do --,"mission","missionoffer" + -- Convert component, suppressing if 0 (hide from md). + ret_table[field] = (ret_table[field] and ret_table[field] > 0 + and ConvertStringTo64Bit(tostring(ret_table[field]))) + end + + -- For texts convenience, since often targetbasename and targetshortname + -- are selected together (former if available, else latter), create a + -- unified term here. + ret_table.texts.targetBaseOrShortName = menu.texts.targetBaseName or menu.texts.targetShortName + + --Lib.Print_Table(ret_table, "interact_menu_params") + --if ret_table.componentMissions ~= nil then + -- Lib.Print_Table(ret_table.componentMissions, "componentMissions") + --end + --Lib.Print_Table(ret_table.texts, "texts") + + -- Package up menu into into a table, and signal to md. + AddUITriggeredEvent("Interact_Menu_API", "onDisplay", ret_table) + + -- Clear any prior recorded temp actions. + L.temp_actions = {} + L.temp_actions_order = {} end + -- Injected code which adds new action entries to the menu. +-- Gets called when menu opened, and when refreshed. function L.Add_Actions() -- Most actions filter based on this flag check, but not all. -- TODO: consider if this should be filtered or not. @@ -179,46 +515,97 @@ function L.Add_Actions() -- Update object flags. L.Update_Flags(menu.componentSlot.component) - for id, action in pairs(L.actions) do + -- Pick actions to show. + -- Ordering will place static ones first, then temp ones. + local action_specs = {} + -- Static actions, with filtering. + for _, id in ipairs(L.static_actions_order) do + local action = L.static_actions[id] -- Skip disabled actions. if action.disabled == 1 then - -- Skip if flag check fails. elseif not L.Check_Flags(action) then - else - -- Table of generic data to return to the call. - local ret_table = { - id = action.id, - component = convertedComponent, - -- Multiple player ships could be selected, presumably - -- including the above component. - -- This list appears to already be convertedComponents, - -- so can return directly. - selectedplayerships = menu.selectedplayerships, - } - - -- Do conditional checks. - -- TODO - - -- Make a new entry. - menu.insertInteractionContent( - action.section, { - type = action.id, - text = action.name, - helpOverlayID = "interactmenu_"..action.id, - -- TODO: anything useful for this text? - helpOverlayText = "", - helpOverlayHighlightOnly = true, - script = function () return L.Interact_Callback(ret_table) end, - -- TODO: conditionally grey out the action. - active = true, - mouseOverText = action.mouseover }) - menu.insertInteractionContent("player_interaction", entry) + action_specs[id] = action + end + end + -- All temp actions; should have been filtered md-side. + for _, id in ipairs(L.temp_actions_order) do + local action = L.temp_actions[id] + action_specs[id] = action + end + + -- pcall the handler for each individual action, so one bad + -- action doesn't suppress all others. + for id, action in pairs(action_specs) do + local success, error = pcall(L.Process_Action, action) + if not success then + DebugError("Interact API error in Process_Action: "..tostring(error)) + end + end +end + +-- Process a single action. This should be pcalled for safety. +function L.Process_Action(action) + -- Table of generic data to return to the call. + -- Reduced down to just id currently. + local ret_table = { + id = action.id, + } + + -- Set up the text name, with icon. + -- Note: attempting to pass over "\27[icon]" text straight from + -- md ran into problems with the \27 becoming some error character, + -- so icon strings are built here instead. + local text = '' + if action.icon and type(action.icon) == "string" then + text = "\27["..action.icon.."] " + end + text = text .. action.text + + -- Set up the mouseover, also with icon support. + local mouseover = nil + if action.mouseover then + mouseover = '' + if action.mouseover_icon and type(action.mouseover_icon) == "string" then + mouseover = "\27["..action.mouseover_icon.."] " end + mouseover = mouseover .. action.mouseover + end + -- To enable dynamic "active" status updates, a wrapper function will + -- be used to look up the active flag each refresh. + -- TODO: this would also need a hook into the menu code to force a + -- widget refresh (since that is when "active" flags are checked). + --local active_check_func = function() + --end + -- Determine if active (eg. not greyed out). + -- If not specified, defaults true. + local active = true + if action.active == false or action.active == 0 then + active = false end + + -- Make a new entry. + menu.insertInteractionContent( + action.section, { + -- Leave type undefined; don't need a ui event on click. + type = nil, + text = text, + text2 = action.text2, + + -- TODO: is this needed or useful? + helpOverlayID = "interactmenu_"..action.id, + helpOverlayText = "", + helpOverlayHighlightOnly = true, + + script = function () + return L.Interact_Callback(action.keep_open, ret_table) + end, + + active = active, + mouseOverText = mouseover }) end -- Convert a value to a boolean true/false. @@ -234,6 +621,7 @@ end -- Returns a table of flag values (true/false) that can be referenced by -- user actions to determine when they show. +-- Note: mostly depricated in favor of md-side checks. function L.Update_Flags(component) -- Note: it is unclear when component, component64, and convertedComponent @@ -246,7 +634,7 @@ function L.Update_Flags(component) local flags = {} -- Verify the component still exists (get occasional log messages - -- if not). + -- if not). Also, this is 0 when eg. opening a menu on a mission. if convertedComponent > 0 then -- Component class checks. @@ -333,6 +721,7 @@ end -- Check an action against the current flags +-- Note: mostly depricated in favor of md-side checks. function L.Check_Flags(action, flags) if debugger.verbose then @@ -356,7 +745,7 @@ function L.Check_Flags(action, flags) -- If no match, failure. if not match_found then if debugger.verbose then - DebugError("Enable condition not matched for action "..action.id) + DebugError("Interact API: Enable condition not matched for action "..action.id) end return false end @@ -368,7 +757,7 @@ function L.Check_Flags(action, flags) for i, flag in ipairs(action.disabled_conditions) do if L.flags[flag_req] then if debugger.verbose then - DebugError("Disable condition matched for action "..action.id) + DebugError("Interact API: Disable condition matched for action "..action.id) end return false end @@ -381,18 +770,22 @@ end -- Handle callbacks when the player selects the action. -function L.Interact_Callback(ret_table) +function L.Interact_Callback(keep_open, ret_table) if debugger.verbose then - Lib.Print_Table(ret_table, "ret_table") + --Lib.Print_Table(ret_table, "ret_table") + DebugError("Interact API: selected custom action: "..tostring(ret_table.id)) + CallEventScripts("directChatMessageReceived", "InteractMenu;lua action: "..ret_table.id) end - --CallEventScripts("directChatMessageReceived", "InteractMenu;lua action: "..ret_table.id) - -- Signal md with the component/object. - AddUITriggeredEvent("Interact_Menu", "Selected", ret_table) + AddUITriggeredEvent("Interact_Menu_API", "Selected", ret_table) + -- Close the context menu. - menu.onCloseElement("close") + -- Note: md may have sent over 'false' as 0. + if not keep_open or keep_open == 0 then + menu.onCloseElement("close") + end end diff --git a/extensions/sn_mod_support_apis/lua/named_pipes/Pipes.lua b/extensions/sn_mod_support_apis/lua/named_pipes/Pipes.lua index 5e0e25f..ca3eef0 100644 --- a/extensions/sn_mod_support_apis/lua/named_pipes/Pipes.lua +++ b/extensions/sn_mod_support_apis/lua/named_pipes/Pipes.lua @@ -132,9 +132,8 @@ function L.Schedule_Read(pipe_name, callback, continuous_read) -- If the read polling function isn't currently active, activate it. if L.read_polling_active == false then -- Do this by hooking into the onUpdate signal, which appears to - -- run every frame. + -- run every frame. Use the time api, which has extra robustness. -- TODO: move this to Poll_For_Reads, and call it once. - --SetScript("onUpdate", L.Poll_For_Reads) Time.Register_NewFrame_Callback(L.Poll_For_Reads) -- Debug printout. @@ -161,9 +160,7 @@ function L.Schedule_Write(pipe_name, callback, message) -- If the write polling function isn't currently active, activate it. if L.write_polling_active == false then - -- Do this by hooking into the onUpdate signal, which appears to - -- run every frame. - --SetScript("onUpdate", L.Poll_For_Writes) + -- Check every frame. Time.Register_NewFrame_Callback(L.Poll_For_Writes) -- Debug printout. diff --git a/extensions/sn_mod_support_apis/lua/simple_menu/Interface.lua b/extensions/sn_mod_support_apis/lua/simple_menu/Interface.lua index 6472a3f..f2714de 100644 --- a/extensions/sn_mod_support_apis/lua/simple_menu/Interface.lua +++ b/extensions/sn_mod_support_apis/lua/simple_menu/Interface.lua @@ -222,28 +222,15 @@ function L._Process_Command(args) DebugError("Processing command: "..args.command) Lib.Print_Table(args, "Args") end + + -- Check the command names, roughly in most to least frequent order. - -- Create a new menu; does not display immediately. - if args.command == "Create_Menu" then - -- Hand off to the standalone menu. - Standalone_Menu.Open(args) - - -- Close the menu if open. - elseif args.command == "Close_Menu" then - -- Hand off to the standalone menu. - Standalone_Menu.Close() - - elseif args.command == "Register_Options_Menu" then - -- Validate all needed args are present. - Lib.Validate_Args(args, { - {n="id"}, - {n="title"}, - {n="columns", t='int'}, - {n="private", t='boolean', d=false}, - }) - -- Hand off. - Options_Menu.Register_Options_Menu(args) - + -- Handle widget update requests. + -- Check this early, since when in use, it may be used at a high rate. + if args.command == "Update_Widget" then + -- Hand off to another function, since code is potentially long. + L.Update_Widget(args) + elseif args.command == "Refresh_Menu" then -- Just skip for now if in wrong mode. if menu_data.mode ~= "options" then @@ -252,6 +239,35 @@ function L._Process_Command(args) Options_Menu.Refresh(args) + elseif args.command == "Make_Widget" then + -- Hand off to the widget maker local function. + L.Make_Widget(args) + + -- Add a new row. + elseif args.command == "Add_Row" then + -- Filter for row properties, defaults, fix bools. + local properties = Lib.Filter_Table(args, widget_properties["row"]) + Lib.Fill_Defaults(properties, menu_data.custom_widget_defaults["row"]) + Lib.Fill_Complex_Defaults(properties, widget_defaults["row"]) + Lib.Fix_Bool_Args(properties, widget_defaults["row"]) + + -- This also supports a custom "selectable" flag, which wasn't fixed + -- by bool handling above. + Lib.Validate_Args(args, { + {n="selectable", t="boolean", d=true}, + }) + + -- Add one generic row. + -- First arg is rowdata; must not be nil/false for the row to + -- be selectable. + -- TODO: set up row event callbacks; maybe use rowdata, given + -- to callbacks, to specify details (eg. row index) to avoid + -- having to look it up. + local new_row = menu_data.ftable:addRow(args.selectable, properties) + -- Store in user row table for each reference. + table.insert(menu_data.user_rows, new_row) + + -- Adjust table aspects. elseif args.command == "Call_Table_Method" then -- Supported table methods and their order of call args. @@ -292,31 +308,6 @@ function L._Process_Command(args) -- Call the method. menu_data.ftable[args.method](table.unpack(method_args)) - - - -- Add a new row. - elseif args.command == "Add_Row" then - -- Filter for row properties, defaults, fix bools. - local properties = Lib.Filter_Table(args, widget_properties["row"]) - Lib.Fill_Defaults(properties, menu_data.custom_widget_defaults["row"]) - Lib.Fill_Complex_Defaults(properties, widget_defaults["row"]) - Lib.Fix_Bool_Args(properties, widget_defaults["row"]) - - -- This also supports a custom "selectable" flag, which wasn't fixed - -- by bool handling above. - Lib.Validate_Args(args, { - {n="selectable", t="boolean", d=true}, - }) - - -- Add one generic row. - -- First arg is rowdata; must not be nil/false for the row to - -- be selectable. - -- TODO: set up row event callbacks; maybe use rowdata, given - -- to callbacks, to specify details (eg. row index) to avoid - -- having to look it up. - local new_row = menu_data.ftable:addRow(args.selectable, properties) - -- Store in user row table for each reference. - table.insert(menu_data.user_rows, new_row) -- Add a submenu link, for options menus. @@ -340,15 +331,27 @@ function L._Process_Command(args) Options_Menu.Add_Submenu_Link(args) - elseif args.command == "Make_Widget" then - -- Hand off to the widget maker local function. - L.Make_Widget(args) + -- Create a new menu; does not display immediately. + elseif args.command == "Create_Menu" then + -- Hand off to the standalone menu. + Standalone_Menu.Open(args) - -- Handle widget update requests. - elseif args.command == "Update_Widget" then - -- Hand off to another function, since code is potentially long. - L.Update_Widget(args) - + -- Close the menu if open. + elseif args.command == "Close_Menu" then + -- Hand off to the standalone menu. + Standalone_Menu.Close() + + elseif args.command == "Register_Options_Menu" then + -- Validate all needed args are present. + Lib.Validate_Args(args, { + {n="id"}, + {n="title"}, + {n="columns", t='int'}, + {n="private", t='boolean', d=false}, + }) + -- Hand off. + Options_Menu.Register_Options_Menu(args) + else -- If here, the command wasn't recognized. error("Simple_Menu.Process_Command: unknown command: "..args.command) diff --git a/extensions/sn_mod_support_apis/lua/simple_menu/Options_Menu.lua b/extensions/sn_mod_support_apis/lua/simple_menu/Options_Menu.lua index f31bf2e..c19a5ef 100644 --- a/extensions/sn_mod_support_apis/lua/simple_menu/Options_Menu.lua +++ b/extensions/sn_mod_support_apis/lua/simple_menu/Options_Menu.lua @@ -9,11 +9,6 @@ the player can pick a specific mod and edit its settings. This solves the question of how mod users will make their menus easily accessible to the player (without relying on the hotkey api or similar). -TODO: hooks to modify stock menu parameters of interest. - - menu.valueGameUIScale, menu.callbackGameUIScaleReset() - for higher scaling (above 1.5) - - menu.valueGfxAA to unlock higher ssaa (probably not useful) - ]] @@ -31,6 +26,7 @@ local T = require("extensions.sn_mod_support_apis.lua.Text") -- Import library functions for strings and tables. local Lib = require("extensions.sn_mod_support_apis.lua.simple_menu.Library") +local Time = require("extensions.sn_mod_support_apis.lua.time.Interface") -- Container for local functions that will be exported. local menu = {} @@ -578,21 +574,12 @@ function menu.Display_Custom_Menu_PostShell(menu_spec, frame, ftable) -- To allow users to make widgets without having to say when to -- display the menu, an automated 1-frame delayed display() can - -- be used. - SetScript("onUpdate", menu.Handle_Delayed_Display) - -- Record the start time of the delay, otherwise the onUpdate may - -- happen in this frame before widgets are set up. - menu.opened_time = GetCurRealTime() + -- be used. This covers the frame gap between lua and md. + Time.Set_Frame_Alarm('options_menu_delay', 1, menu.Handle_Delayed_Display) + end function menu.Handle_Delayed_Display() - -- Make sure a frame has passed. - if menu.opened_time == GetCurRealTime() then return end - - --DebugError("Handle_Delayed_Display called") - - -- Stop listening to updates. - RemoveScript("onUpdate", menu.Handle_Delayed_Display) -- In testing, a user that sets up the options menu onOpen event to -- create a standalone menu caused some confusion here. @@ -622,8 +609,7 @@ function menu.Handle_Delayed_Display() if menu_data.currentTableCol then menu_data.ftable:setSelectedCol(menu_data.currentTableCol) menu_data.currentTableCol = nil - end - + end -- Do the final display call. menu_data.frame:display() @@ -634,9 +620,8 @@ return menu - --[[ -Development notes: +Old development notes: menu.submenuHandler(optionParameter) This function picks which submenu to open, based on what the player clicked diff --git a/extensions/sn_mod_support_apis/lua/time/Interface.lua b/extensions/sn_mod_support_apis/lua/time/Interface.lua index a884390..0cf995d 100644 --- a/extensions/sn_mod_support_apis/lua/time/Interface.lua +++ b/extensions/sn_mod_support_apis/lua/time/Interface.lua @@ -39,8 +39,11 @@ Other lua modules may require() this module to access these api functions: alarm_time is the original scheduled time of the alarm, which will generally be sometime earlier than the current time (due to frame boundaries). +* Set_Frame_Alarm(id, frames, function) + - As above, but measures time in frame switches. An MD ui event is raised on every frame, which MD cues may listen to. +This differs from a cue firing every 1ms in that this works when paused. The event.param3 will be the current engine time. Example: `` @@ -158,11 +161,14 @@ local L = { -- Table of alarms scheduled. Values are the realtime the alarm -- will go off. Keyed by id. alarms = {}, + -- As above, but holding 2-element lists with + -- {realtime when alarm set up, pending frames until alarm}. + frame_alarms = {}, -- Table of lua callbacks per id, for alarms. -- These are used for the lua interface alarms, which use callback -- functions instead of signals. - -- Key ids should match those in "alarms" above. + -- Key ids should match those in "alarms" or "frame_alarms" above. alarm_callbacks = {}, } @@ -304,7 +310,6 @@ end -- Start the alarm poller, if not started yet. function L.Start_Alarm_Polling() if not L.checking_alarms then - --SetScript("onUpdate", L.Poll_For_Alarm) E.Register_NewFrame_Callback(L.Poll_For_Alarm) L.checking_alarms = true if L.debug then @@ -313,6 +318,8 @@ function L.Start_Alarm_Polling() end end + +-- Set up a new time based alarm, from md. function L.Set_Alarm(_, id_time) if L.debug then DebugError("Time.Set_Alarm got: "..tostring(id_time)) @@ -331,7 +338,8 @@ function L.Set_Alarm(_, id_time) L.Start_Alarm_Polling() end --- Lua callable, with lua callback. + +-- Lua callable, with lua callback, time based alarm. function E.Set_Alarm(id, time, callback) -- Schedule the alarm. L.alarms[id] = GetCurRealTime() + time @@ -343,6 +351,29 @@ function E.Set_Alarm(id, time, callback) end +-- Lua callable, alarm based on frame count. +function E.Set_Frame_Alarm(id, frames, callback) + + -- Schedule the alarm for some frames in the future. + -- Note: New_Frame_Detector may have already run this frame, or may have + -- yet to run, or may get skipped this frame entirely (if not started + -- yes). If it is going to still run this frame, then the below timer + -- will need to have an extra +1 offset. While it can be determined + -- if it already ran, it cannot be determined if it will run. + -- The solution: record the current real time, and frames remaining; + -- the polling loop will count down frames, but only once the original + -- time has passed (eg. not on the same frame as registered). + L.frame_alarms[id] = {GetCurRealTime(), frames} + + -- Record the callback. + L.alarm_callbacks[id] = callback + -- Start polling if not already. + L.Start_Alarm_Polling() +end + + +-- Alarm polling, called once each frame while alarms are active, +-- possibly skipping the first frame the first alarm was scheduled. function L.Poll_For_Alarm() local now = GetCurRealTime() @@ -351,11 +382,10 @@ function L.Poll_For_Alarm() -- Flag to indicate there is an alarm that didn't fire yet. local alarms_still_pending = false - -- Check all alarms. + -- Check all alarms for triggers. for id, time in pairs(L.alarms) do if now >= time then - -- Time reached. -- Send back the id and the time that was scheduled, not the -- current time, so that alarms can be chained to make clocks. @@ -374,12 +404,52 @@ function L.Poll_For_Alarm() alarms_still_pending = true end end - -- Remove any alarms that fired. for i, id in ipairs(ids_to_remove) do L.alarms[id] = nil end + -- Check all frame alarms. + ids_to_remove = {} + for id, alarm_data in pairs(L.frame_alarms) do + + -- Unpack the alarm data. + local alarm_start_time, pending_frames = unpack(alarm_data) + + -- If time has advanced since the alarm was set up, count down + -- a frame. + if alarm_start_time ~= now then + + -- If this was the last frame, trigger the alarm. + if pending_frames == 1 then + -- Frame reached. + -- Send back the id only; frame count doesn't make sense. + -- Use a lua callback if known, else an MD signal. + if L.alarm_callbacks[id] ~= nil then + pcall(L.alarm_callbacks[id], id) + else + L.Raise_Signal(id) + end + + -- Note this id for list removal. + table.insert(ids_to_remove, id) + else + -- Otherwise count down the frames. + alarm_data[2] = alarm_data[2] - 1 + -- Note the alarm didn't fire. + alarms_still_pending = true + end + else + -- Note the alarm didn't fire. + alarms_still_pending = true + end + end + -- Remove any alarms that fired. + for i, id in ipairs(ids_to_remove) do + L.frame_alarms[id] = nil + end + + -- If no alarms remaining, stop polling. if not alarms_still_pending then --RemoveScript("onUpdate", L.Poll_For_Alarm) @@ -421,7 +491,8 @@ function L.New_Frame_Detector() local now = GetCurRealTime() -- Skip if this frame already handled. if L.last_frame_time == now then return end - -- Update recorded time and continue. + + -- Update recorded time. L.last_frame_time = now -- Signal MD listeners, returning time for convenience. @@ -442,6 +513,12 @@ function E.Register_NewFrame_Callback(callback) DebugError("Register_Frame_Callback got a non-function: "..type(callback)) return end + -- Ignore if already registered. + for index, value in ipairs(L.frame_callbacks) do + if value == callback then + return + end + end table.insert(L.frame_callbacks, callback) end diff --git a/extensions/sn_mod_support_apis/md/Hotkey_API.xml b/extensions/sn_mod_support_apis/md/Hotkey_API.xml index 52fc7b0..3525f0a 100644 --- a/extensions/sn_mod_support_apis/md/Hotkey_API.xml +++ b/extensions/sn_mod_support_apis/md/Hotkey_API.xml @@ -1027,7 +1027,7 @@ TODO: - diff --git a/extensions/sn_mod_support_apis/md/Interact_Menu_API.xml b/extensions/sn_mod_support_apis/md/Interact_Menu_API.xml index e03260d..9c662b0 100644 --- a/extensions/sn_mod_support_apis/md/Interact_Menu_API.xml +++ b/extensions/sn_mod_support_apis/md/Interact_Menu_API.xml @@ -3,13 +3,16 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > - + + + + @@ -22,6 +25,83 @@ param="'extensions.sn_mod_support_apis.lua.interact_menu.Interface'"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -55,15 +139,265 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -76,19 +410,25 @@ - - @@ -195,8 +535,12 @@ - - + + + + + + @@ -204,6 +548,9 @@ + + + @@ -216,35 +563,142 @@ - - - + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + - - + + + + + + - + + + + + + + - - + - + @@ -252,22 +706,236 @@ - + - + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/sn_mod_support_apis/md/Named_Pipes.xml b/extensions/sn_mod_support_apis/md/Named_Pipes.xml index dd329dc..ec5502d 100644 --- a/extensions/sn_mod_support_apis/md/Named_Pipes.xml +++ b/extensions/sn_mod_support_apis/md/Named_Pipes.xml @@ -642,7 +642,7 @@ TODO: - - + @@ -693,7 +695,7 @@ TODO: --> - + @@ -719,13 +721,15 @@ TODO: - + - + @@ -733,8 +737,7 @@ TODO: - - + diff --git a/extensions/sn_mod_support_apis/md/Simple_Menu_API.xml b/extensions/sn_mod_support_apis/md/Simple_Menu_API.xml index 911be23..8eaf2bb 100644 --- a/extensions/sn_mod_support_apis/md/Simple_Menu_API.xml +++ b/extensions/sn_mod_support_apis/md/Simple_Menu_API.xml @@ -556,7 +556,7 @@ Complex properties: * backgroundColor = 'Helper.color.white' - Color of the background texture. - onCloseElement event event returns: + onCloseElement event returns: * echo, event, id * reason - String, reason for the closure. diff --git a/extensions/sn_mod_support_apis/md/Simple_Menu_Options.xml b/extensions/sn_mod_support_apis/md/Simple_Menu_Options.xml index f4624de..a046a16 100644 --- a/extensions/sn_mod_support_apis/md/Simple_Menu_Options.xml +++ b/extensions/sn_mod_support_apis/md/Simple_Menu_Options.xml @@ -118,7 +118,7 @@ placed, along with any others of the same category. - If not given, or given as "General" will be set to General. * $mouseover - - String, text to display on menu widget mouseover. + - Optional string, text to display on menu widget mouseover. * $type - String, name of the type of widget to create. - "button": an on/off button. @@ -155,15 +155,20 @@ + - + + + + + diff --git a/extensions/sn_mod_support_apis/t/0001.xml b/extensions/sn_mod_support_apis/t/0001.xml index 9bd9c77..a90371f 100644 --- a/extensions/sn_mod_support_apis/t/0001.xml +++ b/extensions/sn_mod_support_apis/t/0001.xml @@ -13,7 +13,7 @@ Enable menu api debug logs Prints extra api status info to the debug log, general category - + Enable hotkey api debug logs Named Pipes API @@ -23,6 +23,10 @@ Status Name of the pipe If the pipe is connected + + Disable interact menu api + Enable interact api debug logs + diff --git a/extensions/test_interact_menu_api/md/Interact_Menu_API_Test.xml b/extensions/test_interact_menu_api/md/Interact_Menu_API_Test.xml index 9455ddf..a602f17 100644 --- a/extensions/test_interact_menu_api/md/Interact_Menu_API_Test.xml +++ b/extensions/test_interact_menu_api/md/Interact_Menu_API_Test.xml @@ -10,15 +10,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -30,10 +90,20 @@ + param="'action;%s'.[ event.param.$id ]"/> + + + + + +