mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
LuaLS meta file generation (scripting.lua) (#6516)
This commit is contained in:
1 parent
ad5b2372fa
commit
4d39287de1
18 files changed
+708
-40
No files matched your search
@@ -261,6 +261,7 @@ Flag exe_params[] =
|
||||
{ "-output_sexps", "Output SEXPs to sexps.html", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-output_sexps", },
|
||||
{ "-output_scripting", "Output scripting to scripting.html", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-output_scripting", },
|
||||
{ "-output_script_json", "Output scripting doc to scripting.json", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-output_script_json", },
|
||||
{ "-output_script_lua", "Output scripting doc to scripting.lua", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-output_script_luastub", },
|
||||
{ "-controlconfig_tbl", "Save control presets to table", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-controlconfig_tbl", },
|
||||
{ "-save_render_target", "Save render targets to file", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-save_render_target", },
|
||||
{ "-verify_vps", "Spew VP CRCs to vp_crcs.txt", true, 0, EASY_DEFAULT, "Dev Tool", "http://www.hard-light.net/wiki/index.php/Command-Line_Reference#-verify_vps", },
|
||||
@@ -577,6 +578,7 @@ cmdline_parm get_flags_arg(GET_FLAGS_STRING, "Output the launcher flags file", A
|
||||
cmdline_parm output_sexp_arg("-output_sexps", NULL, AT_NONE); //WMC - outputs all SEXPs to sexps.html
|
||||
cmdline_parm output_scripting_arg("-output_scripting", NULL, AT_NONE); //WMC
|
||||
cmdline_parm output_script_json_arg("-output_script_json", nullptr, AT_NONE); // m!m
|
||||
cmdline_parm output_script_luastub_arg("-output_script_lua", nullptr, AT_NONE); // mjnmixael
|
||||
cmdline_parm generate_controlconfig_arg("-controlconfig_tbl", nullptr, AT_NONE);
|
||||
|
||||
// Deprecated flags - CommanderDJ
|
||||
@@ -2075,6 +2077,9 @@ bool SetCmdlineParams()
|
||||
if (output_script_json_arg.found() )
|
||||
Output_scripting_json = true;
|
||||
|
||||
if (output_script_luastub_arg.found())
|
||||
Output_scripting_luastub = true;
|
||||
|
||||
if (output_sexp_arg.found() ) {
|
||||
Cmdline_output_sexp_info = true;
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ ADE_FUNC(playGameSound,
|
||||
}
|
||||
}
|
||||
|
||||
ADE_FUNC(playInterfaceSound, l_Audio, "sound index", "Plays a sound from #Interface Sounds in sounds.tbl", "boolean", "True if sound was played, false if not")
|
||||
ADE_FUNC(playInterfaceSound, l_Audio, "number index", "Plays a sound from #Interface Sounds in sounds.tbl", "boolean", "True if sound was played, false if not")
|
||||
{
|
||||
int idx;
|
||||
if(!ade_get_args(L, "i", &idx))
|
||||
|
||||
@@ -58,7 +58,7 @@ ADE_FUNC(listFiles, l_CFile, "string directory, string filter",
|
||||
"Lists all the files in the specified directory matching a filter. The filter must have the format "
|
||||
"\"*<rest>\" (the wildcard has to appear at the start), \"<subfolder>/*<rest>\" (to check subfolder(s)) "
|
||||
"or \"*/*<rest>\" (for a glob search).",
|
||||
"string[]", "A table with all matching files or nil on error")
|
||||
"table", "A table with all matching files or nil on error")
|
||||
{
|
||||
using namespace luacpp;
|
||||
|
||||
|
||||
@@ -95,11 +95,6 @@ ADE_FUNC(__len, l_Graphics_Cameras, NULL, "Gets number of cameras", "number", "N
|
||||
//****SUBLIBRARY: Graphics/Fonts
|
||||
ADE_LIB_DERIV(l_Graphics_Fonts, "Fonts", nullptr, "Font library", l_Graphics);
|
||||
|
||||
ADE_FUNC(__len, l_Graphics_Fonts, NULL, "Number of loaded fonts", "number", "Number of loaded fonts")
|
||||
{
|
||||
return ade_set_args(L, "i", font::FontManager::numberOfFonts());
|
||||
}
|
||||
|
||||
ADE_INDEXER(l_Graphics_Fonts, "number/string IndexOrFilename", "Array of loaded fonts", "font", "Font handle, or invalid font handle if index is invalid")
|
||||
{
|
||||
if (lua_isnumber(L, 2))
|
||||
@@ -129,6 +124,11 @@ ADE_INDEXER(l_Graphics_Fonts, "number/string IndexOrFilename", "Array of loaded
|
||||
}
|
||||
}
|
||||
|
||||
ADE_FUNC(__len, l_Graphics_Fonts, nullptr, "Number of loaded fonts", "number", "Number of loaded fonts")
|
||||
{
|
||||
return ade_set_args(L, "i", font::FontManager::numberOfFonts());
|
||||
}
|
||||
|
||||
ADE_VIRTVAR(CurrentFont, l_Graphics, "font", "Current font", "font", nullptr)
|
||||
{
|
||||
font_h *newFh = nullptr;
|
||||
@@ -550,7 +550,7 @@ ADE_FUNC(setCamera, l_Graphics, "[camera Camera]", "Sets current camera, or rese
|
||||
|
||||
ADE_FUNC(setColor,
|
||||
l_Graphics,
|
||||
"number|color /* red value or color object */, number Green, number Blue, [number Alpha]",
|
||||
"number|color /* red value or color object */, [number Green, number Blue, number Alpha]",
|
||||
"Sets 2D drawing color for the active context; each color number should be from 0 (darkest) to 255 (brightest)",
|
||||
nullptr,
|
||||
nullptr)
|
||||
@@ -584,7 +584,7 @@ ADE_FUNC(setColor,
|
||||
|
||||
ADE_FUNC(getColor,
|
||||
l_Graphics,
|
||||
"boolean",
|
||||
"[boolean]",
|
||||
"Gets the active 2D drawing color from the active context. False to return raw rgb, true to return a color object. Defaults to false.",
|
||||
"number, number, number, number | color",
|
||||
"rgba color which is currently in use for 2D drawing")
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace api {
|
||||
//**********LIBRARY: Mission
|
||||
ADE_LIB(l_Options, "Options", "opt", "Options library");
|
||||
|
||||
ADE_VIRTVAR(Options, l_Options, nullptr, "The available options.", "option[]", "A table of all the options.")
|
||||
ADE_VIRTVAR(Options, l_Options, nullptr, "The available options.", "table", "A table of all the options.")
|
||||
{
|
||||
using namespace luacpp;
|
||||
|
||||
|
||||
@@ -174,7 +174,7 @@ ADE_FUNC(getString, l_Parsing, nullptr, "Gets a single line of text from the fil
|
||||
}
|
||||
}
|
||||
|
||||
ADE_FUNC(getFloat, l_Parsing, nullptr, "Gets a floating point number from the file", "string", "number or nil on error")
|
||||
ADE_FUNC(getFloat, l_Parsing, nullptr, "Gets a floating point number from the file", "number", "number or nil on error")
|
||||
{
|
||||
if (Parse_text == nullptr) {
|
||||
LuaError(L, "Parsing system is currently not active!");
|
||||
@@ -192,7 +192,7 @@ ADE_FUNC(getFloat, l_Parsing, nullptr, "Gets a floating point number from the fi
|
||||
}
|
||||
}
|
||||
|
||||
ADE_FUNC(getInt, l_Parsing, nullptr, "Gets an integer number from the file", "string", "number or nil on error")
|
||||
ADE_FUNC(getInt, l_Parsing, nullptr, "Gets an integer number from the file", "number", "number or nil on error")
|
||||
{
|
||||
if (Parse_text == nullptr) {
|
||||
LuaError(L, "Parsing system is currently not active!");
|
||||
|
||||
@@ -133,13 +133,15 @@ ADE_FUNC(enableInput,
|
||||
return ADE_RETURN_TRUE;
|
||||
}
|
||||
|
||||
ADE_FUNC(disableInput, l_UserInterface, "", "Disables UI input", "boolean", "true if successful")
|
||||
ADE_FUNC(disableInput, l_UserInterface, nullptr, "Disables UI input", nullptr, "nothing")
|
||||
{
|
||||
SCP_UNUSED(L);
|
||||
|
||||
scpui::disableInput();
|
||||
game_flush();
|
||||
Main_hall_poll_key = true;
|
||||
|
||||
return ADE_RETURN_TRUE;
|
||||
return ADE_RETURN_NIL;
|
||||
}
|
||||
|
||||
ADE_VIRTVAR(ColorTags,
|
||||
@@ -320,7 +322,7 @@ ADE_VIRTVAR(ErrorCount, l_UserInterface_PilotSelect, nullptr, "The amount of err
|
||||
}
|
||||
|
||||
ADE_FUNC(enumeratePilots, l_UserInterface_PilotSelect, nullptr,
|
||||
"Lists all pilots available for the pilot selection<br>", "string[]",
|
||||
"Lists all pilots available for the pilot selection<br>", "table",
|
||||
"A table containing the pilots (without a file extension) or nil on error")
|
||||
{
|
||||
using namespace luacpp;
|
||||
@@ -521,7 +523,7 @@ ADE_LIB_DERIV(l_UserInterface_Barracks, "Barracks", nullptr,
|
||||
l_UserInterface);
|
||||
|
||||
ADE_FUNC(listPilotImages, l_UserInterface_Barracks, nullptr, "Lists the names of the available pilot images.",
|
||||
"string[]", "The list of pilot filenames or nil on error")
|
||||
"table", "The list of pilot filenames or nil on error")
|
||||
{
|
||||
pilot_load_pic_list();
|
||||
|
||||
@@ -535,7 +537,7 @@ ADE_FUNC(listPilotImages, l_UserInterface_Barracks, nullptr, "Lists the names of
|
||||
}
|
||||
|
||||
ADE_FUNC(listSquadImages, l_UserInterface_Barracks, nullptr, "Lists the names of the available squad images.",
|
||||
"string[]", "The list of squad filenames or nil on error")
|
||||
"table", "The list of squad filenames or nil on error")
|
||||
{
|
||||
pilot_load_squad_pic_list();
|
||||
|
||||
@@ -548,7 +550,7 @@ ADE_FUNC(listSquadImages, l_UserInterface_Barracks, nullptr, "Lists the names of
|
||||
return ade_set_args(L, "t", &out);
|
||||
}
|
||||
|
||||
ADE_FUNC(acceptPilot, l_UserInterface_Barracks, "player selection, boolean changeState", "Accept the given player as the current player. Set second argument to false to prevent returning to the mainhall",
|
||||
ADE_FUNC(acceptPilot, l_UserInterface_Barracks, "player selection, [boolean changeState]", "Accept the given player as the current player. Set second argument to false to prevent returning to the mainhall",
|
||||
"boolean", "true on success, false otherwise")
|
||||
{
|
||||
player_h* plh;
|
||||
@@ -617,7 +619,7 @@ ADE_FUNC(getCampaignList,
|
||||
l_UserInterface_Campaign,
|
||||
nullptr,
|
||||
"Get the campaign name and description lists",
|
||||
"string[], string[], string[]",
|
||||
"table, table, table",
|
||||
"Three tables with the names, file names, and descriptions of the campaigns")
|
||||
{
|
||||
luacpp::LuaTable nameTable = luacpp::LuaTable::create(L);
|
||||
@@ -2854,7 +2856,7 @@ ADE_FUNC(getPlayerChannel,
|
||||
"string",
|
||||
"Searches for a player and returns if they were found and the channel they are on. Channel is an empty string if "
|
||||
"channel is private or player is not found.",
|
||||
"string string",
|
||||
"string, string",
|
||||
"The response string and the player's channel")
|
||||
{
|
||||
const char* plr_name;
|
||||
@@ -2977,7 +2979,7 @@ ADE_FUNC(__len, l_PXO_Channels, nullptr, "The number of channels available", "nu
|
||||
return ade_set_args(L, "i", static_cast<int>(Multi_pxo_channels.size()));
|
||||
}
|
||||
|
||||
ADE_FUNC(joinPrivateChannel, l_UserInterface_MultiPXO, nullptr, "Joins the specified private channel", nullptr, nullptr)
|
||||
ADE_FUNC(joinPrivateChannel, l_UserInterface_MultiPXO, "string channel", "Joins the specified private channel", nullptr, nullptr)
|
||||
{
|
||||
const char* channel;
|
||||
if (!ade_get_args(L, "s", &channel))
|
||||
@@ -3311,7 +3313,7 @@ ADE_FUNC(sendJoinRequest,
|
||||
{
|
||||
|
||||
bool observer = false;
|
||||
ade_get_args(L, "*|b", &observer);
|
||||
ade_get_args(L, "|b", &observer);
|
||||
|
||||
if (Active_games.empty()) {
|
||||
multi_common_add_notify(XSTR("No games found!", 757));
|
||||
@@ -3736,8 +3738,8 @@ ADE_FUNC(getCountdownTime,
|
||||
l_UserInterface_MultiSync,
|
||||
nullptr,
|
||||
"Gets the current countdown time. Will be -1 before the countdown starts otherwise will be the num seconds until missions starts.",
|
||||
nullptr,
|
||||
nullptr)
|
||||
"number",
|
||||
"The countdown in seconds")
|
||||
{
|
||||
SCP_UNUSED(L);
|
||||
|
||||
|
||||
@@ -301,7 +301,7 @@ ADE_VIRTVAR(Conflicted,
|
||||
l_Control,
|
||||
nullptr,
|
||||
"Whether or not the bound control has a conflict.",
|
||||
"boolean",
|
||||
"string",
|
||||
"Returns the conflict string if true, nil otherwise")
|
||||
{
|
||||
control_h current;
|
||||
|
||||
@@ -108,7 +108,7 @@ ADE_FUNC(read,
|
||||
"<li>\"*a\" - Reads the rest of the file and returns it as a string.</li>"
|
||||
"<li>\"*l\" - Reads a line. Skips the end of line markers.</li>"
|
||||
"<li>(number) - Reads given number of characters, then returns them as a string.</li></ul>",
|
||||
"number|string...",
|
||||
"string",
|
||||
"Requested data, or nil if the function fails")
|
||||
{
|
||||
cfile_h* cfp = nullptr;
|
||||
|
||||
@@ -1284,16 +1284,16 @@ ADE_FUNC(computeDocker, l_Dockingbay, "dockingbay",
|
||||
return ade_set_args(L, "oo", l_Vector.Set(final_pos),l_Matrix.Set(matrix_h(&final_orient)));
|
||||
}
|
||||
|
||||
ADE_FUNC(isValid, l_Dockingbay, NULL, "Detects whether is valid or not", "number", "<i>true</i> if valid, <i>false</i> otherwise")
|
||||
ADE_FUNC(isValid, l_Dockingbay, nullptr, "Detects whether is valid or not", "boolean", "true if valid, false otherwise")
|
||||
{
|
||||
dockingbay_h* dbh = NULL;
|
||||
dockingbay_h* dbh = nullptr;
|
||||
|
||||
if (!ade_get_args(L, "o", l_Dockingbay.GetPtr(&dbh)))
|
||||
{
|
||||
return ADE_RETURN_FALSE;
|
||||
}
|
||||
|
||||
if (dbh == NULL)
|
||||
if (dbh == nullptr)
|
||||
{
|
||||
return ADE_RETURN_FALSE;
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ ADE_VIRTVAR(Description, l_Channel, nullptr, "The description of the channel", "
|
||||
return ade_set_args(L, "s", current.getChannel()->desc);
|
||||
}
|
||||
|
||||
ADE_VIRTVAR(NumPlayers, l_Channel, nullptr, "The number of players in the channel", "string", "The number of players")
|
||||
ADE_VIRTVAR(NumPlayers, l_Channel, nullptr, "The number of players in the channel", "number", "The number of players")
|
||||
{
|
||||
channel_h current;
|
||||
if (!ade_get_args(L, "o", l_Channel.Get(¤t))) {
|
||||
@@ -261,7 +261,7 @@ ADE_VIRTVAR(NumPlayers, l_Channel, nullptr, "The number of players in the channe
|
||||
return ade_set_args(L, "i", static_cast<int>(current.getChannel()->num_users));
|
||||
}
|
||||
|
||||
ADE_VIRTVAR(NumGames, l_Channel, nullptr, "The number of games the channel", "string", "The number of games")
|
||||
ADE_VIRTVAR(NumGames, l_Channel, nullptr, "The number of games the channel", "number", "The number of games")
|
||||
{
|
||||
channel_h current;
|
||||
if (!ade_get_args(L, "o", l_Channel.Get(¤t))) {
|
||||
@@ -1083,7 +1083,7 @@ ADE_VIRTVAR(EndMission,
|
||||
return ADE_RETURN_NIL;
|
||||
}
|
||||
|
||||
ADE_VIRTVAR(SkillLevel, l_NetGame, nullptr, "The current skill level the game, 0-4", "number", "the skill level")
|
||||
ADE_VIRTVAR(SkillLevel, l_NetGame, "number", "The current skill level the game, 0-4", "number", "the skill level")
|
||||
{
|
||||
net_game_h current;
|
||||
int skill = 0;
|
||||
@@ -1098,7 +1098,7 @@ ADE_VIRTVAR(SkillLevel, l_NetGame, nullptr, "The current skill level the game, 0
|
||||
return ade_set_args(L, "i", multi_ho_get_skill_level());
|
||||
}
|
||||
|
||||
ADE_VIRTVAR(RespawnLimit, l_NetGame, nullptr, "The current respawn limit", "number", "the respawn limit")
|
||||
ADE_VIRTVAR(RespawnLimit, l_NetGame, "number", "The current respawn limit", "number", "the respawn limit")
|
||||
{
|
||||
net_game_h current;
|
||||
int respawn = 0;
|
||||
@@ -1116,7 +1116,7 @@ ADE_VIRTVAR(RespawnLimit, l_NetGame, nullptr, "The current respawn limit", "numb
|
||||
return ade_set_args(L, "i", current.getNetgame()->options.respawn);
|
||||
}
|
||||
|
||||
ADE_VIRTVAR(TimeLimit, l_NetGame, nullptr, "The current time limit in minutes. -1 means no limit.", "number", "the time limit")
|
||||
ADE_VIRTVAR(TimeLimit, l_NetGame, "number", "The current time limit in minutes. -1 means no limit.", "number", "the time limit")
|
||||
{
|
||||
net_game_h current;
|
||||
int time = 0;
|
||||
@@ -1136,7 +1136,7 @@ ADE_VIRTVAR(TimeLimit, l_NetGame, nullptr, "The current time limit in minutes. -
|
||||
return ade_set_args(L, "i", current.getNetgame()->options.mission_time_limit);
|
||||
}
|
||||
|
||||
ADE_VIRTVAR(KillLimit, l_NetGame, nullptr, "The current kill limit", "number", "the kill limit")
|
||||
ADE_VIRTVAR(KillLimit, l_NetGame, "number", "The current kill limit", "number", "the kill limit")
|
||||
{
|
||||
net_game_h current;
|
||||
int kill = 0;
|
||||
@@ -1156,7 +1156,7 @@ ADE_VIRTVAR(KillLimit, l_NetGame, nullptr, "The current kill limit", "number", "
|
||||
return ade_set_args(L, "i", current.getNetgame()->options.kill_limit);
|
||||
}
|
||||
|
||||
ADE_VIRTVAR(ObserverLimit, l_NetGame, nullptr, "The current observer limit", "number", "the observer limit")
|
||||
ADE_VIRTVAR(ObserverLimit, l_NetGame, "number", "The current observer limit", "number", "the observer limit")
|
||||
{
|
||||
net_game_h current;
|
||||
int observer = 0;
|
||||
@@ -1176,7 +1176,7 @@ ADE_VIRTVAR(ObserverLimit, l_NetGame, nullptr, "The current observer limit", "nu
|
||||
return ade_set_args(L, "i", current.getNetgame()->options.max_observers);
|
||||
}
|
||||
|
||||
ADE_VIRTVAR(Locked, l_NetGame, nullptr, "Whether or not the loadouts have been locked for the current team. Can be set only by the host or team captain.", "number", "the locked status")
|
||||
ADE_VIRTVAR(Locked, l_NetGame, "boolean", "Whether or not the loadouts have been locked for the current team. Can be set only by the host or team captain.", "boolean", "the locked status")
|
||||
{
|
||||
net_game_h current;
|
||||
bool locked = false;
|
||||
|
||||
@@ -1101,7 +1101,7 @@ ADE_FUNC(renderTechModel2,
|
||||
|
||||
ADE_FUNC(renderSelectModel,
|
||||
l_Weaponclass,
|
||||
"number x, number y, [number width = 629, number height = 355, number currentEffectSetting = default, number zoom = 0.65]",
|
||||
"boolean restart, number x, number y, [number width = 629, number height = 355, number currentEffectSetting = default, number zoom = 0.65]",
|
||||
"Draws the 3D select weapon model with the chosen effect at the specified coordinates. Restart should "
|
||||
"be true on the first frame this is called and false on subsequent frames. Note that primary weapons "
|
||||
"will not render anything if they do not have a valid pof model defined! Valid selection effects are 1 (fs1) or 2 (fs2), "
|
||||
|
||||
@@ -0,0 +1,642 @@
|
||||
#include "doc_html.h"
|
||||
#include "utils/string_utils.h"
|
||||
|
||||
#include <regex>
|
||||
|
||||
namespace scripting {
|
||||
namespace {
|
||||
|
||||
SCP_vector<std::pair<SCP_string, SCP_string>> Hook_variables;
|
||||
SCP_vector<std::pair<SCP_string, SCP_string>> Aliases;
|
||||
|
||||
//Forward declrattion
|
||||
SCP_string get_output_type_link(const ade_type_info& type_info, SCP_string nestedFuncName = "");
|
||||
|
||||
SCP_string sanitizeParamName(const SCP_string& name)
|
||||
{
|
||||
// List of common Lua reserved keywords
|
||||
static const SCP_vector<SCP_string> luaKeywords = {"and",
|
||||
"break",
|
||||
"do",
|
||||
"else",
|
||||
"elseif",
|
||||
"end",
|
||||
"false",
|
||||
"for",
|
||||
"function",
|
||||
"if",
|
||||
"in",
|
||||
"local",
|
||||
"nil",
|
||||
"not",
|
||||
"or",
|
||||
"repeat",
|
||||
"return",
|
||||
"then",
|
||||
"true",
|
||||
"until",
|
||||
"while"};
|
||||
|
||||
// Check if the name matches any Lua keyword
|
||||
if (util::isStringOneOf(name, luaKeywords)) {
|
||||
return name + "Val";
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
SCP_string create_function_alias(const ade_type_info& type_info, SCP_string aliasName = "")
|
||||
{
|
||||
static int dupCount = 1; // Static counter for unique aliases
|
||||
|
||||
if (aliasName.empty() || aliasName.find("param") == 0) {
|
||||
aliasName = "aliasFunc";
|
||||
}
|
||||
|
||||
// Sanitize aliasName
|
||||
for (char& c : aliasName) {
|
||||
if (c == '?') {
|
||||
c = '_';
|
||||
}
|
||||
}
|
||||
|
||||
SCP_string alias = "fun(";
|
||||
|
||||
const auto& elements = type_info.elements();
|
||||
int count = 1;
|
||||
|
||||
// Process function parameters
|
||||
for (auto iter = elements.begin() + 1; iter != elements.end(); ++iter) {
|
||||
if (count > 1) {
|
||||
alias += ", ";
|
||||
}
|
||||
SCP_string paramName = iter->getName().empty() ? "param" + std::to_string(count) : sanitizeParamName(iter->getName());
|
||||
alias += paramName + ": " + get_output_type_link(*iter);
|
||||
count++;
|
||||
}
|
||||
|
||||
// Add return type
|
||||
alias += "): " + get_output_type_link(elements.front());
|
||||
|
||||
// Check for duplicates
|
||||
auto it = std::find_if(Aliases.begin(), Aliases.end(), [&](const auto& entry) {
|
||||
return entry.second == alias;
|
||||
});
|
||||
|
||||
if (it != Aliases.end()) {
|
||||
return it->first; // Alias already exists, reuse it
|
||||
}
|
||||
|
||||
// Check if aliasName conflicts with an existing name
|
||||
auto nameConflict = std::find_if(Aliases.begin(), Aliases.end(), [&](const auto& entry) {
|
||||
return entry.first == aliasName;
|
||||
});
|
||||
|
||||
// Modify aliasName to make it unique if necessary
|
||||
if (nameConflict != Aliases.end()) {
|
||||
aliasName += "_" + std::to_string(dupCount++);
|
||||
}
|
||||
|
||||
Aliases.emplace_back(aliasName, alias);
|
||||
|
||||
return aliasName;
|
||||
}
|
||||
|
||||
SCP_string get_output_type_link(const ade_type_info& type_info, SCP_string nestedFuncName)
|
||||
{
|
||||
SCP_string rstring;
|
||||
|
||||
switch (type_info.getType()) {
|
||||
case ade_type_info_type::Empty:
|
||||
return "nil";
|
||||
|
||||
case ade_type_info_type::Simple:
|
||||
rstring = type_info.getIdentifier();
|
||||
return lcase_equal(rstring, "void") ? "nil" : rstring;
|
||||
|
||||
case ade_type_info_type::Tuple: {
|
||||
bool first = true;
|
||||
for (const auto& type : type_info.elements()) {
|
||||
if (!first)
|
||||
rstring += ", ";
|
||||
first = false;
|
||||
rstring += get_output_type_link(type);
|
||||
}
|
||||
return rstring;
|
||||
}
|
||||
|
||||
case ade_type_info_type::Array:
|
||||
rstring = "{ ";
|
||||
rstring += get_output_type_link(type_info.arrayType());
|
||||
rstring += " ... }";
|
||||
return rstring;
|
||||
|
||||
case ade_type_info_type::Map:
|
||||
rstring = "table";
|
||||
if (!type_info.elements().empty() && type_info.elements().size() == 2) {
|
||||
rstring += "<" + get_output_type_link(type_info.elements()[0]) + ", " +
|
||||
get_output_type_link(type_info.elements()[1]) + ">";
|
||||
}
|
||||
return rstring;
|
||||
|
||||
case ade_type_info_type::Iterator:
|
||||
return get_output_type_link(type_info.arrayType()) + "[]";
|
||||
|
||||
case ade_type_info_type::Alternative: {
|
||||
bool first = true;
|
||||
for (const auto& type : type_info.elements()) {
|
||||
if (!first)
|
||||
rstring += " | ";
|
||||
first = false;
|
||||
rstring += get_output_type_link(type);
|
||||
}
|
||||
return rstring;
|
||||
}
|
||||
|
||||
case ade_type_info_type::Function:
|
||||
return create_function_alias(type_info, std::move(nestedFuncName));
|
||||
|
||||
case ade_type_info_type::Generic: {
|
||||
rstring = get_output_type_link(type_info.elements().front());
|
||||
if (type_info.elements().size() > 1) {
|
||||
rstring += "<";
|
||||
bool first = true;
|
||||
for (auto iter = type_info.elements().begin() + 1; iter != type_info.elements().end(); ++iter) {
|
||||
if (!first)
|
||||
rstring += ", ";
|
||||
first = false;
|
||||
rstring += get_output_type_link(*iter);
|
||||
}
|
||||
rstring += ">";
|
||||
}
|
||||
return rstring;
|
||||
}
|
||||
|
||||
case ade_type_info_type::Varargs:
|
||||
return get_output_type_link(type_info.elements().front());
|
||||
|
||||
default:
|
||||
Assertion(false, "Unhandled type!");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
SCP_string escapeNewlines(const SCP_string& str)
|
||||
{
|
||||
SCP_string result = str;
|
||||
std::replace(result.begin(), result.end(), '\r', ' '); // Replace carriage returns
|
||||
std::replace(result.begin(), result.end(), '\n', ' '); // Replace newlines
|
||||
return result;
|
||||
}
|
||||
|
||||
SCP_string buildFullPath(const SCP_vector<const DocumentationElement*>& parents, bool func = false)
|
||||
{
|
||||
SCP_string fullPath;
|
||||
if (!parents.empty()) {
|
||||
for (size_t i = 0; i < parents.size(); ++i) {
|
||||
SCP_string name = parents[i]->shortName.empty() ? parents[i]->name : parents[i]->shortName; // Use shortName if available, otherwise name
|
||||
fullPath += name;
|
||||
if (i < parents.size() - 1) { // Not the last element
|
||||
fullPath += ".";
|
||||
} else if (func && parents[i]->type == ElementType::Class) { // Last element and is a class
|
||||
fullPath += ":";
|
||||
} else { // Last element and is not a class (e.g., library)
|
||||
fullPath += ".";
|
||||
}
|
||||
}
|
||||
}
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
// This is kind of a hacky check but it's the best we can do given the quirks of the FSO API
|
||||
bool isTableLikeLibrary(const DocumentationElement* el)
|
||||
{
|
||||
if (!el) {
|
||||
return false; // Null pointer safety check
|
||||
}
|
||||
|
||||
return el->shortName.empty() && !el->children.empty() && util::isStringOneOf(el->children[0]->name, {"__indexer", "__len"});
|
||||
}
|
||||
|
||||
// Function to output a Library
|
||||
void outputLibrary(FILE* fp, const DocumentationElement* el, const SCP_vector<const DocumentationElement*>& parents)
|
||||
{
|
||||
SCP_string fullPath = buildFullPath(parents);
|
||||
SCP_string libraryName = el->shortName.empty() ? el->name : el->shortName;
|
||||
|
||||
if (isTableLikeLibrary(el)) {
|
||||
// Document as a table of objects
|
||||
const auto first = static_cast<DocumentationElementFunction*>(el->children.front().get());
|
||||
fprintf(fp, "\n--- @field %s ", el->name.c_str());
|
||||
fputs(get_output_type_link(first->returnType).c_str(), fp);
|
||||
fprintf(fp, "[] # %s - %s", escapeNewlines(first->description).c_str(), escapeNewlines(first->returnDocumentation).c_str());
|
||||
} else {
|
||||
// Document as a regular library
|
||||
fprintf(fp, "\n\n--- %s: %s", el->name.c_str(), escapeNewlines(el->description).c_str());
|
||||
fprintf(fp, "\n--- @class %s", el->name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Function to output a Class
|
||||
void outputClass(FILE* fp, const DocumentationElement* el)
|
||||
{
|
||||
SCP_string escapedDescription;
|
||||
if (!el->description.empty()) {
|
||||
escapedDescription = escapeNewlines(el->description);
|
||||
}
|
||||
|
||||
SCP_string parentClass;
|
||||
|
||||
// If the class can also access the members of the Object class
|
||||
// This list may be incomplete
|
||||
if (util::isStringOneOf(el->name, {"asteroid", "debris", "fireball", "ship", "waypoint", "weapon"})) {
|
||||
parentClass = " : object";
|
||||
}
|
||||
|
||||
// Enumeration class also works like integer apparently
|
||||
if (el->name == "enumeration") {
|
||||
parentClass = " : integer";
|
||||
}
|
||||
|
||||
fprintf(fp, "\n\n-- %s: %s", el->name.c_str(), escapedDescription.c_str());
|
||||
fprintf(fp, "\n%s = {}", el->name.c_str());
|
||||
fprintf(fp, "\n--- @class %s%s", el->name.c_str(), parentClass.c_str());
|
||||
}
|
||||
|
||||
// Function to output a Function/Operator or a Metamethod
|
||||
void outputFunction(FILE* fp, const DocumentationElement* el, const SCP_vector<const DocumentationElement*>& parents)
|
||||
{
|
||||
const auto funcEl = static_cast<const DocumentationElementFunction*>(el);
|
||||
if (funcEl) {
|
||||
for (const auto& overload : funcEl->overloads) {
|
||||
SCP_string functionName = el->name.empty() ? el->shortName : el->name;
|
||||
|
||||
// Functions
|
||||
if (functionName.rfind("__", 0) != 0) {
|
||||
// Handle regular functions
|
||||
fprintf(fp, "\n--- @field %s fun(", functionName.c_str());
|
||||
|
||||
// Check if the most immediate parent is a Library
|
||||
bool isLibraryParent = !parents.empty() && parents.back()->type == ElementType::Library;
|
||||
|
||||
int count = 1;
|
||||
if (!isLibraryParent) {
|
||||
// Include self: self if the parent is not a Library
|
||||
// because our class-like objects always require the
|
||||
// object: func syntax and this ensures that's validated
|
||||
fputs("self: self", fp);
|
||||
count++;
|
||||
}
|
||||
|
||||
// List function arguments
|
||||
for (const auto& arg : overload.arguments) {
|
||||
if (count > 1) {
|
||||
fputs(", ", fp);
|
||||
}
|
||||
SCP_string paramName = arg.name.empty() ? "param" + std::to_string(count) : arg.name;
|
||||
|
||||
paramName = sanitizeParamName(paramName);
|
||||
|
||||
if (arg.optional) {
|
||||
paramName += "?";
|
||||
}
|
||||
|
||||
if (arg.type.getType() == ade_type_info_type::Varargs) {
|
||||
paramName = "...";
|
||||
}
|
||||
|
||||
fprintf(fp, "%s: ", paramName.c_str());
|
||||
fputs(get_output_type_link(arg.type, paramName).c_str(), fp);
|
||||
count++;
|
||||
}
|
||||
|
||||
// Metamethods
|
||||
} else {
|
||||
// List of metamethods supported by LuaLS
|
||||
static const std::vector<std::string> supportedMetamethods = {
|
||||
"__unm", // Unary minus
|
||||
"__bnot", // Bitwise NOT
|
||||
"__len", // Length
|
||||
"__add", // Addition
|
||||
"__sub", // Subtraction
|
||||
"__mul", // Multiplication
|
||||
"__div", // Division
|
||||
"__mod", // Modulo
|
||||
"__pow", // Exponentiation
|
||||
"__idiv", // Floor division
|
||||
"__band", // Bitwise AND
|
||||
"__bor", // Bitwise OR
|
||||
"__bxor", // Bitwise XOR
|
||||
"__shl", // Bitwise LEFT SHIFT
|
||||
"__shr", // Bitwise RIGHT SHIFT
|
||||
"__concat", // Concatenation
|
||||
"__call", // Call
|
||||
"__indexer" // Indexing
|
||||
};
|
||||
// Check if the metamethod is supported
|
||||
if (!util::isStringOneOf(functionName, supportedMetamethods)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Indexer is special
|
||||
if (functionName == "__indexer") {
|
||||
fputs("\n--- @field [", fp);
|
||||
} else {
|
||||
|
||||
functionName.erase(0, 2);
|
||||
fprintf(fp, "\n--- @operator %s(", functionName.c_str());
|
||||
}
|
||||
|
||||
// Documentation seems to only allow up to one argument for a metamethod
|
||||
if (!overload.arguments.empty()) {
|
||||
fputs(get_output_type_link(overload.arguments[0].type).c_str(), fp);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Return type
|
||||
if (functionName == "__indexer") {
|
||||
fputs("] ", fp);
|
||||
} else {
|
||||
fputs("): ", fp);
|
||||
}
|
||||
fputs(get_output_type_link(funcEl->returnType).c_str(), fp);
|
||||
|
||||
if (el->description.find("or nil") != std::string::npos ||
|
||||
el->description.find("nil if") != std::string::npos) {
|
||||
fputs("?", fp);
|
||||
}
|
||||
|
||||
if (!el->deprecationMessage.empty()) {
|
||||
SCP_string escapedMessage = escapeNewlines(el->deprecationMessage);
|
||||
SCP_string version = gameversion::format_version(el->deprecationVersion);
|
||||
fprintf(fp, " # DEPRECATED %s: %s -- ", version.c_str(), escapedMessage.c_str());
|
||||
}
|
||||
|
||||
if (!el->description.empty()) {
|
||||
SCP_string escapedDescription = escapeNewlines(el->description);
|
||||
fprintf(fp, " # %s", escapedDescription.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Function to output a Property
|
||||
void outputProperty(FILE* fp, const DocumentationElement* el)
|
||||
{
|
||||
if (el) {
|
||||
const auto propEl = static_cast<const DocumentationElementProperty*>(el);
|
||||
SCP_string propName = propEl->name.empty() ? propEl->shortName : propEl->name;
|
||||
|
||||
if (propEl->description.find("or nil") != std::string::npos ||
|
||||
propEl->description.find("nil if") != std::string::npos)
|
||||
{
|
||||
propName += "?";
|
||||
}
|
||||
|
||||
fprintf(fp, "\n--- @field %s ", propName.c_str());
|
||||
fputs(get_output_type_link(propEl->getterType).c_str(), fp);
|
||||
|
||||
if (!propEl->returnDocumentation.empty()) {
|
||||
fprintf(fp, " # %s, ", propEl->returnDocumentation.c_str());
|
||||
}
|
||||
|
||||
if (!el->deprecationMessage.empty()) {
|
||||
SCP_string escapedMessage = escapeNewlines(el->deprecationMessage);
|
||||
SCP_string version = gameversion::format_version(el->deprecationVersion);
|
||||
fprintf(fp, " # DEPRECATED %s: %s -- ", version.c_str(), escapedMessage.c_str());
|
||||
}
|
||||
|
||||
fprintf(fp, " %s", propEl->description.empty() ? "No description available." : propEl->description.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void parseFunctionSignature(SCP_string& input)
|
||||
{
|
||||
// Regular expression to match the input format
|
||||
static std::regex functionRegex(R"(function\((.*?)\s(\w+)\)\s->\s(.+))");
|
||||
std::smatch match;
|
||||
|
||||
if (std::regex_match(input, match, functionRegex)) {
|
||||
// Extract parameter types, parameter name, and return type
|
||||
SCP_string paramTypes = match[1].str(); // e.g., "number | string | nil"
|
||||
SCP_string paramName = match[2].str(); // e.g., "result"
|
||||
SCP_string returnType = match[3].str(); // e.g., "nil"
|
||||
|
||||
// Format the output as desired
|
||||
input = "fun(" + paramName + ": " + paramTypes + "): " + returnType;
|
||||
}
|
||||
}
|
||||
|
||||
void OutputElement(FILE* fp, const std::unique_ptr<DocumentationElement>& el, const SCP_vector<const DocumentationElement*>& parents)
|
||||
{
|
||||
if (!el || (el->name.empty() && el->shortName.empty())) {
|
||||
Warning(LOCATION, "Found ade_table_entry with no name or shortname");
|
||||
return;
|
||||
}
|
||||
|
||||
// First output any child libraries, excluding table-like libraries
|
||||
for (const auto& child : el->children) {
|
||||
if (child->type == ElementType::Library && !isTableLikeLibrary(child.get())) {
|
||||
SCP_vector<const DocumentationElement*> newParents(parents);
|
||||
newParents.push_back(el.get());
|
||||
OutputElement(fp, child, newParents);
|
||||
}
|
||||
}
|
||||
|
||||
switch (el->type) {
|
||||
case ElementType::Library:
|
||||
outputLibrary(fp, el.get(), parents);
|
||||
break;
|
||||
case ElementType::Class:
|
||||
outputClass(fp, el.get());
|
||||
break;
|
||||
case ElementType::Function:
|
||||
case ElementType::Operator:
|
||||
outputFunction(fp, el.get(), parents);
|
||||
break;
|
||||
case ElementType::Property:
|
||||
outputProperty(fp, el.get());
|
||||
break;
|
||||
default:
|
||||
Warning(LOCATION, "Unknown type '%d' passed to ade_table_entry::OutputMeta", static_cast<int>(el->type));
|
||||
break;
|
||||
}
|
||||
|
||||
// Now output everything else for non table-like libraries
|
||||
// If we're a library and we're table-like, then do not print any children
|
||||
// because we've been defined as an array already and that would be redundant
|
||||
if (el->type != ElementType::Library || !isTableLikeLibrary(el.get())) {
|
||||
for (const auto& child : el->children) {
|
||||
|
||||
// If the child is anything but a regular library then output it, including table-like libraries
|
||||
if (child->type != ElementType::Library || isTableLikeLibrary(child.get())) {
|
||||
SCP_vector<const DocumentationElement*> newParents(parents);
|
||||
newParents.push_back(el.get());
|
||||
OutputElement(fp, child, newParents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For Hook Variables we output all the possible HVs as fields of the HV class
|
||||
// This allows the Lua environment to pick them up as valid variables with defined
|
||||
// return types. Scripters should refer to scripting.html to know when they are valid
|
||||
if (el->name == "HookVariables") {
|
||||
for (const auto& var : Hook_variables) {
|
||||
SCP_string type = var.second;
|
||||
if (type.find("function") != SCP_string::npos) {
|
||||
parseFunctionSignature(type);
|
||||
}
|
||||
fprintf(fp, "\n--- @field %s? %s", var.first.c_str(), type.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// After processing all children, initialize the table if necessary
|
||||
if (el->type == ElementType::Library && !isTableLikeLibrary(el.get())) {
|
||||
SCP_string fullPath = buildFullPath(parents);
|
||||
SCP_string libraryName = el->shortName.empty() ? el->name : el->shortName;
|
||||
fprintf(fp, "\n%s%s = {}", fullPath.c_str(), libraryName.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void OutputLuaMeta(FILE* fp, const ScriptingDocumentation& doc)
|
||||
{
|
||||
// First pass: Output classes
|
||||
for (const auto& el : doc.elements) {
|
||||
if (el && el->type == ElementType::Class) {
|
||||
SCP_vector<const DocumentationElement*> emptyParents;
|
||||
OutputElement(fp, el, emptyParents);
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: Output everything else (libraries, functions, fields, etc.)
|
||||
for (const auto& el : doc.elements) {
|
||||
if (el && el->type != ElementType::Class) { // Skip classes this time
|
||||
SCP_vector<const DocumentationElement*> emptyParents;
|
||||
OutputElement(fp, el, emptyParents);
|
||||
}
|
||||
}
|
||||
|
||||
fputs("\n", fp);
|
||||
}
|
||||
|
||||
void OutputLuaJsonDocumentation(FILE* fp) {
|
||||
if (!fp) return;
|
||||
|
||||
fputs("--- dkjson is a built-in lua method for encoding and decoding values to/from json files\n", fp);
|
||||
fputs("--- call dkjson with <local json = require('dkjson')>\n", fp);
|
||||
fputs("--- @class json\n", fp);
|
||||
fprintf(fp, "--- @field encode json_encode\n");
|
||||
fprintf(fp, "--- @field decode json_decode\n");
|
||||
fprintf(fp, "--- @field use_lpeg json_use_lpeg\n");
|
||||
fprintf(fp, "--- @field quotestring json_quote_string\n");
|
||||
fprintf(fp, "--- @field addnewline json_addnewline\n");
|
||||
fprintf(fp, "--- @field encodeexception json_encodeexception\n");
|
||||
fputs("json = {}\n\n", fp);
|
||||
|
||||
fputs("--- Encodes a Lua value into a JSON string.\n", fp);
|
||||
fputs("---- value: any The Lua value to encode (e.g., table, string, number, boolean, nil).\n", fp);
|
||||
fputs("---- state: table (Optional) A table to configure encoding options:\n", fp);
|
||||
fputs("--- - `indent`: Enables pretty-printing if set to true.\n", fp);
|
||||
fputs("--- - `level`: Sets the current indentation level (used internally).\n", fp);
|
||||
fputs("--- - `keyorder`: Specifies a custom order for object keys.\n", fp);
|
||||
fputs("--- @return string The JSON string representation of the Lua value.\n", fp);
|
||||
fputs("--- @alias json_encode fun(value: any, state?: table): string\n\n", fp);
|
||||
|
||||
fputs("--- Decodes a JSON string into a Lua value.\n", fp);
|
||||
fputs("---- jsonString: string The JSON string to decode.\n", fp);
|
||||
fputs("---- pos: number (Optional) The position in the string to start decoding (default is 1).\n", fp);
|
||||
fputs("---- nullval: any (Optional) A value to represent JSON `null` (default is `nil`).\n", fp);
|
||||
fputs("---- ...: table (Optional) Custom metatables for objects and arrays.\n", fp);
|
||||
fputs("--- @return any The Lua value resulting from the decoding.\n", fp);
|
||||
fputs("--- @return number The position in the string where parsing ended.\n", fp);
|
||||
fputs("--- @return string (Optional) An error message if decoding failed.\n", fp);
|
||||
fputs("--- @alias json_decode fun(jsonString: string, pos?: number, nullval?: any, ...?: table): any, number, string\n\n", fp);
|
||||
|
||||
fputs("--- Enables the use of LPeg for JSON parsing and encoding.\n", fp);
|
||||
fputs("--- @return table The `dkjson` library with LPeg integration enabled.\n", fp);
|
||||
fputs("--- @alias json_use_lpeg fun(): table\n\n", fp);
|
||||
|
||||
fputs("--- Encodes a Lua string as a JSON string literal.\n", fp);
|
||||
fputs("---- value: string The Lua string to encode.\n", fp);
|
||||
fputs("--- @return string The JSON string literal representation of the Lua string.\n", fp);
|
||||
fputs("--- @alias json_quote_string fun(value: string): string\n\n", fp);
|
||||
|
||||
fputs("--- Adds a newline and indentation to the JSON buffer.\n", fp);
|
||||
fputs("---- state: table The current encoding state.\n", fp);
|
||||
fputs("--- @alias json_addnewline fun(state: table): nil\n\n", fp);
|
||||
|
||||
fputs("--- Handles encoding exceptions by returning a placeholder JSON string.\n", fp);
|
||||
fputs("---- reason: string The reason for the exception.\n", fp);
|
||||
fputs("---- value: any The value that caused the exception.\n", fp);
|
||||
fputs("---- state: table The current encoding state.\n", fp);
|
||||
fputs("---- defaultmessage: string The default error message.\n", fp);
|
||||
fputs("--- @return string A JSON string representing the exception.\n", fp);
|
||||
fputs("--- @alias json_encodeexception fun(reason: string, value: any, state: table, defaultmessage: string): string\n", fp);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void output_luastub_doc(const ScriptingDocumentation& doc, const SCP_string& filename)
|
||||
{
|
||||
FILE* fp = fopen(filename.c_str(), "w");
|
||||
|
||||
if (fp == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Header
|
||||
fprintf(fp, "-- Lua Stub File\n");
|
||||
fprintf(fp, "-- Generated for FSO v%s (%s)\n\n", FS_VERSION_FULL, doc.name.c_str());
|
||||
|
||||
//***Version info
|
||||
fprintf(fp, "-- Lua Version: %s\n", LUA_RELEASE);
|
||||
fputs("---@meta\n", fp);
|
||||
|
||||
// Get all the hook variables to print and save them to the vector
|
||||
std::unordered_set<std::string> uniqueHookVars;
|
||||
for (const auto& action : doc.actions) {
|
||||
if (!action.description.empty()) {
|
||||
for (const auto& param : action.parameters) {
|
||||
if (uniqueHookVars.insert(param.name).second) { // Insert returns false if already exists
|
||||
Hook_variables.emplace_back(param.name, param.type.getIdentifier());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OutputLuaMeta(fp, doc);
|
||||
|
||||
fputs("\n-- Aliases help with nested function calls in the above pseudo classes", fp);
|
||||
for (const auto& alias : Aliases) {
|
||||
fprintf(fp, "\n--- @alias %s %s", alias.first.c_str(), alias.second.c_str());
|
||||
}
|
||||
|
||||
fputs("\n", fp);
|
||||
fputs("\n", fp);
|
||||
|
||||
//***Enumerations
|
||||
fprintf(fp, "-- Enumerations\n");
|
||||
for (const auto& enumeration : doc.enumerations) {
|
||||
// Cyborg17 -- Omit the deprecated flag
|
||||
if (enumeration.name == "VM_EXTERNAL_CAMERA_LOCKED") {
|
||||
continue;
|
||||
}
|
||||
// WMC - For now, print to the file without the description.
|
||||
fprintf(fp, "--- @const %s\n", enumeration.name.c_str());
|
||||
fprintf(fp, "%s = enumeration\n", enumeration.name.c_str());
|
||||
}
|
||||
fputs("\n", fp);
|
||||
|
||||
OutputLuaJsonDocumentation(fp);
|
||||
fputs("\n", fp);
|
||||
|
||||
fclose(fp);
|
||||
|
||||
Hook_variables.clear();
|
||||
Aliases.clear();
|
||||
}
|
||||
|
||||
} // namespace scripting
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "scripting_doc.h"
|
||||
|
||||
namespace scripting {
|
||||
|
||||
void output_luastub_doc(const ScriptingDocumentation& doc, const SCP_string& filename);
|
||||
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "parse/parselo.h"
|
||||
#include "scripting/doc_html.h"
|
||||
#include "scripting/doc_json.h"
|
||||
#include "scripting/doc_luastub.h"
|
||||
#include "scripting/global_hooks.h"
|
||||
#include "scripting/scripting_doc.h"
|
||||
#include "ship/ship.h"
|
||||
@@ -32,6 +33,7 @@ using namespace scripting;
|
||||
script_state Script_system("FS2_Open Scripting");
|
||||
bool Output_scripting_meta = false;
|
||||
bool Output_scripting_json = false;
|
||||
bool Output_scripting_luastub = false;
|
||||
bool Scripting_game_init_run = false;
|
||||
|
||||
flag_def_list Script_conditions[] =
|
||||
@@ -154,7 +156,7 @@ void script_init()
|
||||
mprintf(("SCRIPTING: Beginning Lua initialization...\n"));
|
||||
Script_system.CreateLuaState();
|
||||
|
||||
if (Output_scripting_meta || Output_scripting_json) {
|
||||
if (Output_scripting_meta || Output_scripting_json || Output_scripting_luastub) {
|
||||
const auto doc = Script_system.OutputDocumentation([](const SCP_string& error) {
|
||||
mprintf(("Scripting documentation: Error while parsing\n%s(This is only relevant for coders)\n\n",
|
||||
error.c_str()));
|
||||
@@ -168,6 +170,10 @@ void script_init()
|
||||
mprintf(("SCRIPTING: Outputting scripting metadata in JSON format...\n"));
|
||||
scripting::output_json_doc(doc, "scripting.json");
|
||||
}
|
||||
if (Output_scripting_luastub) {
|
||||
mprintf(("SCRIPTING: Outputting scripting metadata in LUA Stub format...\n"));
|
||||
scripting::output_luastub_doc(doc, "scripting.lua");
|
||||
}
|
||||
}
|
||||
|
||||
mprintf(("SCRIPTING: Beginning main hook parse sequence....\n"));
|
||||
|
||||
@@ -351,6 +351,7 @@ void script_init();
|
||||
extern class script_state Script_system;
|
||||
extern bool Output_scripting_meta;
|
||||
extern bool Output_scripting_json;
|
||||
extern bool Output_scripting_luastub;
|
||||
extern bool Scripting_game_init_run;
|
||||
|
||||
//*************************Conditional scripting*************************
|
||||
|
||||
@@ -1267,6 +1267,8 @@ add_file_folder("Scripting"
|
||||
scripting/doc_html.h
|
||||
scripting/doc_json.cpp
|
||||
scripting/doc_json.h
|
||||
scripting/doc_luastub.cpp
|
||||
scripting/doc_luastub.h
|
||||
scripting/doc_parser.cpp
|
||||
scripting/doc_parser.h
|
||||
scripting/global_hooks.cpp
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
|
||||
namespace util {
|
||||
|
||||
std::vector<std::string> split_string(const std::string& s, char delim) {
|
||||
std::vector<std::string> split_string(const std::string& s, char delim)
|
||||
{
|
||||
std::vector<std::string> elems;
|
||||
split_string(s, delim, std::back_inserter(elems));
|
||||
return elems;
|
||||
|
||||
Reference in new issue
Block a user