mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 22:56:30 -10:00
Since the original release of FS2, internal strings have been translated to their English entries in strings.tbl but external strings have not been translated to their English entries in tstrings.tbl. It appears that string translation was universal until it was disabled via a hack, probably due to FRED idiosyncrasies (see comment in `fred_init()`), and then not re-enabled for release. PR #2343 re-enabled it, but this turned out to have undesirable side-effects since many mods used incorrect XSTR indexes for external strings. This PR restores the previous behavior and also cleanly separates localization into translated and untranslated modes. FRED will always run in untranslated mode. FSO will run in translated mode for all languages except English, where it will run in untranslated mode UNLESS the new option `$Use tabled strings for the default language` is enabled in game_settings.tbl.
444 lines
14 KiB
C++
444 lines
14 KiB
C++
//
|
|
//
|
|
|
|
#include <freespace.h>
|
|
#include <gamesequence/gamesequence.h>
|
|
#include <globalincs/version.h>
|
|
#include <network/multi.h>
|
|
#include <parse/parselo.h>
|
|
#include <pilotfile/pilotfile.h>
|
|
#include <playerman/player.h>
|
|
|
|
#include "base.h"
|
|
|
|
#include "scripting/api/objs/vecmath.h"
|
|
#include "scripting/api/objs/gamestate.h"
|
|
#include "scripting/api/objs/player.h"
|
|
#include "scripting/api/objs/enums.h"
|
|
#include "scripting/api/objs/control_info.h"
|
|
#include "scripting/api/objs/gameevent.h"
|
|
|
|
|
|
namespace scripting {
|
|
namespace api {
|
|
|
|
//**********LIBRARY: Base
|
|
ADE_LIB(l_Base, "Base", "ba", "Base FreeSpace 2 functions");
|
|
|
|
ADE_FUNC(print, l_Base, "string Message", "Prints a string", NULL, NULL)
|
|
{
|
|
mprintf(("%s", lua_tostring(L, -1)));
|
|
|
|
return ADE_RETURN_NIL;
|
|
}
|
|
|
|
ADE_FUNC(warning, l_Base, "string Message", "Displays a FreeSpace warning (debug build-only) message with the string provided", NULL, NULL)
|
|
{
|
|
Warning(LOCATION, "%s", lua_tostring(L, -1));
|
|
|
|
return ADE_RETURN_NIL;
|
|
}
|
|
|
|
ADE_FUNC(error, l_Base, "string Message", "Displays a FreeSpace error message with the string provided", NULL, NULL)
|
|
{
|
|
Error(LOCATION, "%s", lua_tostring(L, -1));
|
|
|
|
return ADE_RETURN_NIL;
|
|
}
|
|
|
|
ADE_FUNC(createOrientation, l_Base, "[p/r1c1, b/r1c2, h/r1c3, r2c1, r2c2, r2c3, r3c1, r3c2, r3c3]", "Given 0, 3, or 9 arguments, creates an orientation object with that orientation.", "orientation", "New orientation object, or null orientation on failure")
|
|
{
|
|
matrix m;
|
|
int numargs = ade_get_args(L, "|fffffffff", &m.a1d[0], &m.a1d[1], &m.a1d[2], &m.a1d[3], &m.a1d[4], &m.a1d[5], &m.a1d[6], &m.a1d[7], &m.a1d[8]);
|
|
if(!numargs)
|
|
{
|
|
return ade_set_args(L, "o", l_Matrix.Set( matrix_h(&vmd_identity_matrix) ));
|
|
}
|
|
else if(numargs == 3)
|
|
{
|
|
angles a = {m.a1d[0], m.a1d[1], m.a1d[2]};
|
|
return ade_set_args(L, "o", l_Matrix.Set(matrix_h(&a)));
|
|
}
|
|
else if(numargs == 9)
|
|
{
|
|
return ade_set_args(L, "o", l_Matrix.Set(matrix_h(&m)));
|
|
}
|
|
|
|
return ade_set_error(L, "o", l_Matrix.Set(matrix_h()));
|
|
}
|
|
|
|
ADE_FUNC(createVector, l_Base, "[x, y, z]", "Creates a vector object", "vector", "Vector object")
|
|
{
|
|
vec3d v3 = vmd_zero_vector;
|
|
ade_get_args(L, "|fff", &v3.xyz.x, &v3.xyz.y, &v3.xyz.z);
|
|
|
|
return ade_set_args(L, "o", l_Vector.Set(v3));
|
|
}
|
|
|
|
ADE_FUNC(getFrametimeOverall, l_Base, NULL, "The overall frame time in seconds since the engine has started", "number", "Overall time (seconds)")
|
|
{
|
|
return ade_set_args(L, "x", game_get_overall_frametime());
|
|
}
|
|
|
|
ADE_FUNC(getFrametime, l_Base, "[Do not adjust for time compression (Boolean)]", "Gets how long this frame is calculated to take. Use it to for animations, physics, etc to make incremental changes.", "number", "Frame time (seconds)")
|
|
{
|
|
bool b=false;
|
|
ade_get_args(L, "|b", &b);
|
|
|
|
return ade_set_args(L, "f", b ? flRealframetime : flFrametime);
|
|
}
|
|
|
|
ADE_FUNC(getCurrentGameState, l_Base, "[Depth (number)]", "Gets current FreeSpace state; if a depth is specified, the state at that depth is returned. (IE at the in-game options game, a depth of 1 would give you the game state, while the function defaults to 0, which would be the options screen.", "gamestate", "Current game state at specified depth, or invalid handle if no game state is active yet")
|
|
{
|
|
int depth = 0;
|
|
ade_get_args(L, "|i", &depth);
|
|
|
|
if(depth > gameseq_get_depth())
|
|
return ade_set_args(L, "o", l_GameState.Set(gamestate_h()));
|
|
|
|
return ade_set_args(L, "o", l_GameState.Set(gamestate_h(gameseq_get_state(depth))));
|
|
}
|
|
|
|
ADE_FUNC(getCurrentMPStatus, l_Base, "NIL", "Gets this computers current MP status", "string", "Current MP status" )
|
|
{
|
|
if ( MULTIPLAYER_MASTER )
|
|
return ade_set_args(L, "s", "MULTIPLAYER_MASTER");
|
|
|
|
if ( MULTIPLAYER_HOST )
|
|
return ade_set_args(L, "s", "MULTIPLAYER_HOST");
|
|
|
|
if ( MULTIPLAYER_CLIENT )
|
|
return ade_set_args(L, "s", "MULTIPLAYER_CLIENT");
|
|
|
|
if ( MULTIPLAYER_STANDALONE )
|
|
return ade_set_args(L, "s", "MULTIPLAYER_STANDALONE");
|
|
|
|
return ade_set_args(L, "s", "SINGLEPLAYER");
|
|
}
|
|
|
|
ADE_FUNC(getCurrentPlayer, l_Base, NULL, "Gets a handle of the currently used player.<br><b>Note:</b> If there is no current player then the first player will be returned, check the game state to make sure you have a valid player handle.", "player", "Player handle")
|
|
{
|
|
return ade_set_args(L, "o", l_Player.Set(player_h(Players[Player_num])));
|
|
}
|
|
|
|
ADE_FUNC(loadPlayer, l_Base, "string callsign", "Loads the player with the specified callsign.", "player",
|
|
"Player handle or invalid handle on load failure")
|
|
{
|
|
const char* callsign;
|
|
if (!ade_get_args(L, "s", &callsign)) {
|
|
return ade_set_error(L, "o", l_Player.Set(player_h()));
|
|
}
|
|
|
|
player plr;
|
|
plr.reset();
|
|
pilotfile loader;
|
|
if (!loader.load_player(callsign, &plr)) {
|
|
return ade_set_error(L, "o", l_Player.Set(player_h()));
|
|
}
|
|
|
|
return ade_set_args(L, "o", l_Player.Set(player_h(plr)));
|
|
}
|
|
|
|
ADE_FUNC(savePlayer, l_Base, "player plr", "Saves the specified player.", "boolean",
|
|
"true of successfull, false otherwise")
|
|
{
|
|
player_h* plh;
|
|
if (!ade_get_args(L, "o", l_Player.GetPtr(&plh))) {
|
|
return ADE_RETURN_FALSE;
|
|
}
|
|
|
|
pilotfile loader;
|
|
return ade_set_args(L, "b", loader.save_player(plh->get()));
|
|
}
|
|
|
|
ADE_FUNC(setControlMode, l_Base, "NIL or enumeration LE_*_CONTROL", "Sets the current control mode for the game.", "string", "Current control mode")
|
|
{
|
|
enum_h *e = NULL;
|
|
if (!(ade_get_args(L, "|o", l_Enum.GetPtr(&e)))) {
|
|
if (lua_game_control & LGC_NORMAL)
|
|
return ade_set_args(L, "s", "NORMAL");
|
|
else if (lua_game_control & LGC_STEERING)
|
|
return ade_set_args(L, "s", "STEERING");
|
|
else if (lua_game_control & LGC_FULL)
|
|
return ade_set_args(L, "s", "FULL");
|
|
else
|
|
return ade_set_error(L, "s", "");
|
|
}
|
|
|
|
if (!e) {
|
|
return ade_set_error(L, "s", "");
|
|
}
|
|
|
|
switch (e->index) {
|
|
case LE_NORMAL_CONTROLS:
|
|
lua_game_control |= LGC_NORMAL;
|
|
lua_game_control &= ~(LGC_STEERING|LGC_FULL);
|
|
return ade_set_args(L, "s", "NORMAL CONTROLS");
|
|
case LE_LUA_STEERING_CONTROLS:
|
|
lua_game_control |= LGC_STEERING;
|
|
lua_game_control &= ~(LGC_NORMAL|LGC_FULL);
|
|
return ade_set_args(L, "s", "LUA STEERING CONTROLS");
|
|
case LE_LUA_FULL_CONTROLS:
|
|
lua_game_control |= LGC_FULL;
|
|
lua_game_control &= ~(LGC_STEERING|LGC_NORMAL);
|
|
return ade_set_args(L, "s", "LUA FULL CONTROLS");
|
|
default:
|
|
return ade_set_error(L, "s", "");
|
|
}
|
|
}
|
|
|
|
ADE_FUNC(setButtonControlMode, l_Base, "NIL or enumeration LE_*_BUTTON_CONTROL", "Sets the current control mode for the game.", "string", "Current control mode")
|
|
{
|
|
enum_h *e = NULL;
|
|
if (!(ade_get_args(L, "|o", l_Enum.GetPtr(&e)))) {
|
|
if (lua_game_control & LGC_B_NORMAL)
|
|
return ade_set_args(L, "s", "NORMAL");
|
|
else if (lua_game_control & LGC_B_OVERRIDE)
|
|
return ade_set_args(L, "s", "OVERRIDE");
|
|
else if (lua_game_control & LGC_B_ADDITIVE)
|
|
return ade_set_args(L, "s", "ADDITIVE");
|
|
else
|
|
return ade_set_error(L, "s", "");
|
|
}
|
|
|
|
if (!e) {
|
|
return ade_set_error(L, "s", "");
|
|
}
|
|
|
|
switch (e->index) {
|
|
case LE_NORMAL_BUTTON_CONTROLS:
|
|
lua_game_control |= LGC_B_NORMAL;
|
|
lua_game_control &= ~(LGC_B_ADDITIVE|LGC_B_OVERRIDE);
|
|
return ade_set_args(L, "s", "NORMAL BUTTON CONTROL");
|
|
case LE_LUA_ADDITIVE_BUTTON_CONTROL:
|
|
lua_game_control |= LGC_B_ADDITIVE;
|
|
lua_game_control &= ~(LGC_B_NORMAL|LGC_B_OVERRIDE);
|
|
return ade_set_args(L, "s", "LUA ADDITIVE BUTTON CONTROL");
|
|
case LE_LUA_OVERRIDE_BUTTON_CONTROL:
|
|
lua_game_control |= LGC_B_OVERRIDE;
|
|
lua_game_control &= ~(LGC_B_ADDITIVE|LGC_B_NORMAL);
|
|
return ade_set_args(L, "s", "LUA OVERRIDE BUTTON CONTROL");
|
|
default:
|
|
return ade_set_error(L, "s", "");
|
|
}
|
|
}
|
|
|
|
ADE_FUNC(getControlInfo, l_Base, nullptr, "Gets the control info handle.", "control_info", "control info handle")
|
|
{
|
|
return ade_set_args(L, "o", l_Control_Info.Set(1));
|
|
}
|
|
|
|
ADE_FUNC(setTips, l_Base, "True or false", "Sets whether to display tips of the day the next time the current pilot enters the mainhall.", NULL, NULL)
|
|
{
|
|
if (Player == NULL)
|
|
return ADE_RETURN_NIL;
|
|
|
|
bool tips = false;
|
|
|
|
ade_get_args(L, "b", &tips);
|
|
|
|
if (tips)
|
|
Player->tips = 1;
|
|
else
|
|
Player->tips = 0;
|
|
|
|
return ADE_RETURN_NIL;
|
|
}
|
|
|
|
ADE_FUNC(getGameDifficulty, l_Base, nullptr,
|
|
"Returns the difficulty level from 1-5, 1 being the lowest, (Very Easy) and 5 being the highest (Insane)",
|
|
"number", "Difficulty level as integer")
|
|
{
|
|
return ade_set_args(L, "i", Game_skill_level+1);
|
|
}
|
|
|
|
ADE_FUNC(postGameEvent, l_Base, "gameevent Event", "Sets current game event. Note that you can crash FreeSpace 2 by posting an event at an improper time, so test extensively if you use it.", "boolean", "True if event was posted, false if passed event was invalid")
|
|
{
|
|
gameevent_h *gh = NULL;
|
|
if(!ade_get_args(L, "o", l_GameEvent.GetPtr(&gh)))
|
|
return ade_set_error(L, "b", false);
|
|
|
|
if(!gh->IsValid())
|
|
return ade_set_error(L, "b", false);
|
|
|
|
if (Multi_options_g.pxo)
|
|
Multi_options_g.protocol = NET_TCP;
|
|
psnet_use_protocol(Multi_options_g.protocol);
|
|
|
|
gameseq_post_event(gh->Get());
|
|
|
|
return ADE_RETURN_TRUE;
|
|
}
|
|
|
|
ADE_FUNC(XSTR,
|
|
l_Base,
|
|
"string text, number id",
|
|
"Gets the translated version of text with the given id. "
|
|
"The uses the tstrings table for performing the translation. Passing -1 as the id will always return the given text.",
|
|
"string",
|
|
"The translated text") {
|
|
const char* text = nullptr;
|
|
int id = -1;
|
|
|
|
if (!ade_get_args(L, "si", &text, &id)) {
|
|
return ADE_RETURN_NIL;
|
|
}
|
|
|
|
SCP_string xstr;
|
|
sprintf(xstr, "XSTR(\"%s\", %d)", text, id);
|
|
|
|
SCP_string translated;
|
|
lcl_ext_localize(xstr, translated);
|
|
|
|
return ade_set_args(L, "s", translated.c_str());
|
|
}
|
|
|
|
ADE_FUNC(inMissionEditor, l_Base, nullptr, "Determine if the current script is running in the mission editor (e.g. FRED2). This should be used to control which code paths will be executed even if running in the editor.", "boolean", "true when we are in the mission editor, false otherwise") {
|
|
return ade_set_args(L, "b", Fred_running != 0);
|
|
}
|
|
|
|
ADE_FUNC(isEngineVersionAtLeast,
|
|
l_Base,
|
|
"number major, number minor, number build[, number revision = 0]",
|
|
"Checks if the current version of the engine is at least the specified version. This can be used to check if a feature introduced in a later version of the engine is available.",
|
|
"boolean",
|
|
"true if the version is at least the specified version. false otherwise.") {
|
|
int major = 0;
|
|
int minor = 0;
|
|
int build = 0;
|
|
int revision = 0;
|
|
|
|
if (!ade_get_args(L, "iii|i", &major, &minor, &build, &revision)) {
|
|
return ade_set_error(L, "b", false);
|
|
}
|
|
|
|
auto version = gameversion::version(major, minor, build, revision);
|
|
|
|
return ade_set_args(L, "b", gameversion::check_at_least(version));
|
|
}
|
|
|
|
ADE_FUNC(getCurrentLanguage,
|
|
l_Base,
|
|
nullptr,
|
|
"Determines the language that is being used by the engine. This returns the full name of the language (e.g. \"English\").",
|
|
"string",
|
|
"The current game language") {
|
|
auto lang_name = (Lcl_current_lang == LCL_UNTRANSLATED) ? "UNTRANSLATED" : Lcl_languages[Lcl_current_lang].lang_name;
|
|
return ade_set_args(L, "s", lang_name);
|
|
}
|
|
|
|
ADE_FUNC(getCurrentLanguageExtension,
|
|
l_Base,
|
|
nullptr,
|
|
"Determines the file extension of the language that is being used by the engine. "
|
|
"This returns a short code for the current language that can be used for creating language specific file names (e.g. \"gr\" when the current language is German). "
|
|
"This will return an empty string for the default language.",
|
|
"string",
|
|
"The current game language") {
|
|
int lang = (Lcl_current_lang == LCL_UNTRANSLATED) ? LCL_DEFAULT : Lcl_current_lang;
|
|
return ade_set_args(L, "s", Lcl_languages[lang].lang_ext);
|
|
}
|
|
|
|
ADE_FUNC(getVersionString, l_Base, nullptr,
|
|
"Returns a string describing the version of the build that is currently running. This is mostly intended to "
|
|
"be displayed to the user and not processed by a script so don't rely on the exact format of the string.",
|
|
"string", "The version information")
|
|
{
|
|
auto str = gameversion::get_version_string();
|
|
return ade_set_args(L, "s", str.c_str());
|
|
}
|
|
|
|
ADE_VIRTVAR(MultiplayerMode, l_Base, "boolean", "Determines if the game is currently in single- or multiplayer mode",
|
|
"boolean",
|
|
"true if in multiplayer mode, false if in singleplayer. If neither is the case (e.g. on game init) nil "
|
|
"will be returned")
|
|
{
|
|
bool b;
|
|
if (!ade_get_args(L, "*|b", &b)) {
|
|
return ADE_RETURN_NIL;
|
|
}
|
|
|
|
if (ADE_SETTING_VAR) {
|
|
if (b) {
|
|
Game_mode &= ~GM_NORMAL;
|
|
Game_mode |= GM_MULTIPLAYER;
|
|
} else {
|
|
Game_mode &= ~GM_MULTIPLAYER;
|
|
Game_mode |= GM_NORMAL;
|
|
}
|
|
}
|
|
|
|
if (Game_mode & GM_MULTIPLAYER) {
|
|
return ADE_RETURN_TRUE;
|
|
} else if (Game_mode & GM_NORMAL) {
|
|
return ADE_RETURN_FALSE;
|
|
} else {
|
|
return ADE_RETURN_NIL;
|
|
}
|
|
}
|
|
|
|
//**********SUBLIBRARY: Base/Events
|
|
ADE_LIB_DERIV(l_Base_Events, "GameEvents", NULL, "Freespace 2 game events", l_Base);
|
|
|
|
ADE_INDEXER(l_Base_Events, "number Index/string Name", "Array of game events", "gameevent", "Game event, or invalid gameevent handle if index is invalid")
|
|
{
|
|
const char* name;
|
|
if(!ade_get_args(L, "*s", &name))
|
|
return ade_set_error(L, "o", l_GameEvent.Set(gameevent_h()));
|
|
|
|
int idx = gameseq_get_event_idx(name);
|
|
|
|
if(idx < 0)
|
|
{
|
|
idx = atoi(name);
|
|
|
|
//Lua-->FS2
|
|
idx--;
|
|
|
|
if(idx < 0 || idx >= Num_gs_event_text)
|
|
return ade_set_error(L, "o", l_GameEvent.Set(gameevent_h()));
|
|
}
|
|
|
|
return ade_set_args(L, "o", l_GameEvent.Set(gameevent_h(idx)));
|
|
}
|
|
|
|
ADE_FUNC(__len, l_Base_Events, NULL, "Number of events", "number", "Number of events")
|
|
{
|
|
return ade_set_args(L, "i", Num_gs_event_text);
|
|
}
|
|
|
|
//**********SUBLIBRARY: Base/States
|
|
ADE_LIB_DERIV(l_Base_States, "GameStates", NULL, "Freespace 2 states", l_Base);
|
|
|
|
ADE_INDEXER(l_Base_States, "number Index/string Name", "Array of game states", "gamestate", "Game state, or invalid gamestate handle if index is invalid")
|
|
{
|
|
const char* name;
|
|
if(!ade_get_args(L, "*s", &name))
|
|
return ade_set_error(L, "o", l_GameState.Set(gamestate_h()));
|
|
|
|
int idx = gameseq_get_state_idx(name);
|
|
|
|
if(idx < 0)
|
|
{
|
|
idx = atoi(name);
|
|
|
|
//Lua-->FS2
|
|
idx--;
|
|
|
|
if(idx < 0 || idx >= Num_gs_state_text)
|
|
return ade_set_error(L, "o", l_GameState.Set(gamestate_h()));
|
|
}
|
|
|
|
return ade_set_args(L, "o", l_GameState.Set(gamestate_h(idx)));
|
|
}
|
|
|
|
ADE_FUNC(__len, l_Base_States, NULL, "Number of states", "number", "Number of states")
|
|
{
|
|
return ade_set_args(L, "i", Num_gs_state_text);
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|