1 // 2 // 3 4 #include "base.h" 5 6 #include "globalincs/version.h" 7 8 #include "freespace.h" 9 10 #include "gamesequence/gamesequence.h" 11 #include "network/multi.h" 12 #include "parse/parselo.h" 13 #include "pilotfile/pilotfile.h" 14 #include "playerman/player.h" 15 #include "scripting/api/objs/bytearray.h" 16 #include "scripting/api/objs/control_info.h" 17 #include "scripting/api/objs/enums.h" 18 #include "scripting/api/objs/gameevent.h" 19 #include "scripting/api/objs/gamestate.h" 20 #include "scripting/api/objs/player.h" 21 #include "scripting/api/objs/vecmath.h" 22 #include "scripting/util/LuaValueDeserializer.h" 23 #include "scripting/util/LuaValueSerializer.h" 24 #include "utils/Random.h" 25 26 namespace scripting { 27 namespace api { 28 using Random = ::util::Random; 29 30 //**********LIBRARY: Base 31 ADE_LIB(l_Base, "Base", "ba", "Base FreeSpace 2 functions"); 32 33 ADE_FUNC(print, l_Base, "string Message", "Prints a string", NULL, NULL) 34 { 35 mprintf(("%s", lua_tostring(L, -1))); 36 37 return ADE_RETURN_NIL; 38 } 39 40 ADE_FUNC(warning, l_Base, "string Message", "Displays a FreeSpace warning (debug build-only) message with the string provided", NULL, NULL) 41 { 42 Warning(LOCATION, "%s", lua_tostring(L, -1)); 43 44 return ADE_RETURN_NIL; 45 } 46 47 ADE_FUNC(error, l_Base, "string Message", "Displays a FreeSpace error message with the string provided", NULL, NULL) 48 { 49 Error(LOCATION, "%s", lua_tostring(L, -1)); 50 51 return ADE_RETURN_NIL; 52 } 53 54 ADE_FUNC(rand32, 55 l_Base, 56 "[number a, number b]", 57 "Calls FSO's Random::next() function, which is higher-quality than Lua's ANSI C math.random(). If called with no arguments, returns a random integer from [0, 0x7fffffff]. If called with one argument, returns an integer from [0, a). If called with two arguments, returns an integer from [a, b].", 58 "number", 59 "A random integer") 60 { 61 int a, b; 62 int numargs = ade_get_args(L, "|ii", &a, &b); 63 64 int result; 65 if (numargs == 2) { 66 if (a <= b) { 67 result = Random::next(a, b); 68 } else { 69 LuaError(L, "rand32() script function was passed an invalid range (%d ... %d)!", a, b); 70 result = a; // match behavior of rand_sexp() 71 } 72 } else if (numargs == 1) { 73 if (a > 0) { 74 result = Random::next(a); 75 } else { 76 LuaError(L, "rand32() script function was passed an invalid modulus (%d)!", a); 77 result = 0; 78 } 79 } else { 80 result = Random::next(); 81 } 82 83 return ade_set_error(L, "i", result); 84 } 85 86 ADE_FUNC(rand32f, 87 l_Base, 88 "[number max]", 89 "Calls FSO's Random::next() function and transforms the result to a float. If called with no arguments, returns a random float from [0.0, 1.0). If called with one argument, returns a float from [0.0, max).", 90 "number", 91 "A random float") 92 { 93 float _max; 94 int numargs = ade_get_args(L, "|f", &_max); 95 96 float result = (float)Random::next() * Random::INV_F_MAX_VALUE; 97 98 if (numargs > 0) 99 result *= _max; 100 101 return ade_set_error(L, "f", _max); 102 } 103 104 ADE_FUNC(createOrientation, 105 l_Base, 106 ade_overload_list({nullptr, 107 "number p, number b, number h", 108 "number r1c1, number r1c2, number r1c3, number r2c1, number r2c2, number r2c3, number r3c1, number r3c2, " 109 "number r3c3"}), 110 "Given 0 arguments, creates an identity orientation; 3 arguments, creates an orientation from pitch/bank/heading (in radians); 9 arguments, creates an orientation from a 3x3 row-major order matrix.", 111 "orientation", 112 "New orientation object, or the identity orientation on failure") 113 { 114 matrix m; 115 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]); 116 if(!numargs) 117 { 118 return ade_set_args(L, "o", l_Matrix.Set( matrix_h(&vmd_identity_matrix) )); 119 } 120 else if(numargs == 3) 121 { 122 angles a = {m.a1d[0], m.a1d[1], m.a1d[2]}; 123 return ade_set_args(L, "o", l_Matrix.Set(matrix_h(&a))); 124 } 125 else if(numargs == 9) 126 { 127 return ade_set_args(L, "o", l_Matrix.Set(matrix_h(&m))); 128 } 129 130 return ade_set_error(L, "o", l_Matrix.Set(matrix_h())); 131 } 132 133 ADE_FUNC(createOrientationFromVectors, l_Base, "[vector fvec, vector uvec, vector rvec]", 134 "Given 0 to 3 arguments, creates an orientation object from 0 to 3 vectors. (This is essentially a wrapper for the vm_vector_2_matrix function.) If supplied 0 arguments, this will return the identity orientation. The first vector, if supplied, must be non-null.", 135 "orientation", 136 "New orientation object, or the identity orientation on failure") 137 { 138 vec3d *fvec = nullptr, *uvec = nullptr, *rvec = nullptr; 139 int numargs = ade_get_args(L, "|ooo", l_Vector.GetPtr(&fvec), l_Vector.GetPtr(&uvec), l_Vector.GetPtr(&rvec)); 140 if (!numargs) 141 { 142 return ade_set_args(L, "o", l_Matrix.Set(matrix_h(&vmd_identity_matrix))); 143 } 144 else 145 { 146 // if we have any vectors, the first one should be non-null 147 if (fvec == nullptr) 148 return ade_set_error(L, "o", l_Matrix.Set(matrix_h())); 149 150 matrix m; 151 vm_vector_2_matrix(&m, fvec, uvec, rvec); 152 return ade_set_args(L, "o", l_Matrix.Set(matrix_h(&m))); 153 } 154 } 155 156 ADE_FUNC(createVector, l_Base, "[number x, number y, number z]", "Creates a vector object", "vector", "Vector object") 157 { 158 vec3d v3 = vmd_zero_vector; 159 ade_get_args(L, "|fff", &v3.xyz.x, &v3.xyz.y, &v3.xyz.z); 160 161 return ade_set_args(L, "o", l_Vector.Set(v3)); 162 } 163 164 ADE_FUNC(createRandomVector, l_Base, nullptr, "Creates a fairly random normalized vector object.", "vector", "Vector object") 165 { 166 vec3d v3; 167 vm_vec_rand_vec(&v3); 168 return ade_set_args(L, "o", l_Vector.Set(v3)); 169 } 170 171 ADE_FUNC(createSurfaceNormal, 172 l_Base, 173 "vector point1, vector point2, vector point3", 174 "Determines the surface normal of the plane defined by three points. Returns a normalized vector.", 175 "vector", 176 "The surface normal, or NIL if a handle is invalid") 177 { 178 vec3d *p0 = nullptr, *p1 = nullptr, *p2 = nullptr; 179 if (!ade_get_args(L, "ooo", l_Vector.GetPtr(&p0), l_Vector.GetPtr(&p1), l_Vector.GetPtr(&p2))) 180 return ADE_RETURN_NIL; 181 182 vec3d dest; 183 vm_vec_normal(&dest, p0, p1, p2); 184 return ade_set_args(L, "o", l_Vector.Set(dest)); 185 } 186 187 ADE_FUNC(findIntersection, 188 l_Base, 189 "vector line1_point1, vector line1_point2, vector line2_point1, vector line2_point2", 190 "Determines the point at which two lines intersect. (The lines are assumed to extend infinitely in both directions; the intersection will not necessarily be between the points.)", 191 "vector, number", 192 "Returns two arguments. The first is the point of intersection, if it exists and is unique (otherwise it will be NIL). The second is the find_intersection return value: 0 for a unique intersection, -1 if the lines are colinear, and -2 if the lines do not intersect.") 193 { 194 vec3d *p0 = nullptr, *p0_end = nullptr, *p1 = nullptr, *p1_end = nullptr; 195 if (!ade_get_args(L, "oooo", l_Vector.GetPtr(&p0), l_Vector.GetPtr(&p0_end), l_Vector.GetPtr(&p1), l_Vector.GetPtr(&p1_end))) 196 return ADE_RETURN_NIL; 197 198 // note: we must translate from this API's two-points method to the code's API of a reference point and a direction vector 199 vec3d v0, v1; 200 vm_vec_sub(&v0, p0_end, p0); 201 vm_vec_sub(&v1, p1_end, p1); 202 203 float scalar; 204 int retval = find_intersection(&scalar, p0, p1, &v0, &v1); 205 206 if (retval == 0) 207 { 208 // per comments: 209 // If you want the coords of the intersection, scale v0 by s, then add p0. 210 vm_vec_scale(&v0, scalar); 211 vm_vec_add2(&v0, p0); 212 213 return ade_set_args(L, "oi", l_Vector.Set(v0), retval); 214 } 215 else 216 return ade_set_args(L, "*i", retval); 217 } 218 219 ADE_FUNC(findPointOnLineNearestSkewLine, 220 l_Base, 221 "vector line1_point1, vector line1_point2, vector line2_point1, vector line2_point2", 222 "Determines the point on line 1 closest to line 2 when the lines are skew (non-intersecting in 3D space). (The lines are assumed to extend infinitely in both directions; the point will not necessarily be between the other points.)", 223 "vector", 224 "The closest point, or NIL if a handle is invalid") 225 { 226 vec3d *p0 = nullptr, *p0_end = nullptr, *p1 = nullptr, *p1_end = nullptr; 227 if (!ade_get_args(L, "oooo", l_Vector.GetPtr(&p0), l_Vector.GetPtr(&p0_end), l_Vector.GetPtr(&p1), l_Vector.GetPtr(&p1_end))) 228 return ADE_RETURN_NIL; 229 230 // note: we must translate from this API's two-points method to the code's API of a reference point and a direction vector 231 vec3d v0, v1; 232 vm_vec_sub(&v0, p0_end, p0); 233 vm_vec_sub(&v1, p1_end, p1); 234 235 vec3d dest; 236 find_point_on_line_nearest_skew_line(&dest, p0, &v0, p1, &v1); 237 return ade_set_args(L, "o", l_Vector.Set(dest)); 238 } 239 240 ADE_FUNC(getFrametimeOverall, l_Base, NULL, "The overall frame time in seconds since the engine has started", "number", "Overall time (seconds)") 241 { 242 return ade_set_args(L, "x", game_get_overall_frametime()); 243 } 244 245 ADE_FUNC(getMissionFrametime, l_Base, nullptr, "Gets how long this frame is calculated to take. Use it to for animations, physics, etc to make incremental changes. Increased or decreased based on current time compression", "number", "Frame time (seconds)") 246 { 247 return ade_set_args(L, "f", flFrametime); 248 } 249 250 ADE_FUNC(getRealFrametime, l_Base, nullptr, "Gets how long this frame is calculated to take in real time. Not affected by time compression.", "number", "Frame time (seconds)") 251 { 252 return ade_set_args(L, "f", flRealframetime); 253 } 254 255 ADE_FUNC_DEPRECATED(getFrametime, l_Base, 256 "[boolean adjustForTimeCompression]", 257 "Gets how long this frame is calculated to take. Use it to for animations, physics, etc to make incremental changes.", 258 "number", "Frame time (seconds)", 259 gameversion::version(20, 2, 0, 0), 260 "The parameter of this function is inverted from the naming (passing true returns non-adjusted time). Please use either getMissionFrametime() or getRealFrametime().") 261 { 262 bool b=false; 263 ade_get_args(L, "|b", &b); 264 265 return ade_set_args(L, "f", b ? flRealframetime : flFrametime); 266 } 267 268 ADE_FUNC(getCurrentGameState, l_Base, "[number depth]", "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") 269 { 270 int depth = 0; 271 ade_get_args(L, "|i", &depth); 272 273 if(depth > gameseq_get_depth()) 274 return ade_set_args(L, "o", l_GameState.Set(gamestate_h())); 275 276 return ade_set_args(L, "o", l_GameState.Set(gamestate_h(gameseq_get_state(depth)))); 277 } 278 279 ADE_FUNC(getCurrentMPStatus, l_Base, nullptr, "Gets this computers current MP status", "string", "Current MP status" ) 280 { 281 if ( MULTIPLAYER_MASTER ) 282 return ade_set_args(L, "s", "MULTIPLAYER_MASTER"); 283 284 if ( MULTIPLAYER_HOST ) 285 return ade_set_args(L, "s", "MULTIPLAYER_HOST"); 286 287 if ( MULTIPLAYER_CLIENT ) 288 return ade_set_args(L, "s", "MULTIPLAYER_CLIENT"); 289 290 if ( MULTIPLAYER_STANDALONE ) 291 return ade_set_args(L, "s", "MULTIPLAYER_STANDALONE"); 292 293 return ade_set_args(L, "s", "SINGLEPLAYER"); 294 } 295 296 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") 297 { 298 return ade_set_args(L, "o", l_Player.Set(player_h(&Players[Player_num]))); 299 } 300 301 ADE_FUNC(loadPlayer, l_Base, "string callsign", "Loads the player with the specified callsign.", "player", 302 "Player handle or invalid handle on load failure") 303 { 304 const char* callsign; 305 if (!ade_get_args(L, "s", &callsign)) { 306 return ade_set_error(L, "o", l_Player.Set(player_h())); 307 } 308 309 player plr; 310 plr.reset(); 311 pilotfile loader; 312 if (!loader.load_player(callsign, &plr)) { 313 return ade_set_error(L, "o", l_Player.Set(player_h())); 314 } 315 316 return ade_set_args(L, "o", l_Player.Set(player_h(plr))); 317 } 318 319 ADE_FUNC(savePlayer, l_Base, "player plr", "Saves the specified player.", "boolean", 320 "true of successfull, false otherwise") 321 { 322 player_h* plh; 323 if (!ade_get_args(L, "o", l_Player.GetPtr(&plh))) { 324 return ADE_RETURN_FALSE; 325 } 326 327 pilotfile loader; 328 return ade_set_args(L, "b", loader.save_player(plh->get())); 329 } 330 331 ADE_FUNC(setControlMode, 332 l_Base, 333 "nil|enumeration mode /* LE_*_CONTROL */", 334 "Sets the current control mode for the game.", 335 "string", 336 "Current control mode") 337 { 338 enum_h *e = NULL; 339 if (!(ade_get_args(L, "|o", l_Enum.GetPtr(&e)))) { 340 if (lua_game_control & LGC_NORMAL) 341 return ade_set_args(L, "s", "NORMAL"); 342 else if (lua_game_control & LGC_STEERING) 343 return ade_set_args(L, "s", "STEERING"); 344 else if (lua_game_control & LGC_FULL) 345 return ade_set_args(L, "s", "FULL"); 346 else 347 return ade_set_error(L, "s", ""); 348 } 349 350 if (!e) { 351 return ade_set_error(L, "s", ""); 352 } 353 354 switch (e->index) { 355 case LE_NORMAL_CONTROLS: 356 lua_game_control |= LGC_NORMAL; 357 lua_game_control &= ~(LGC_STEERING|LGC_FULL); 358 return ade_set_args(L, "s", "NORMAL CONTROLS"); 359 case LE_LUA_STEERING_CONTROLS: 360 lua_game_control |= LGC_STEERING; 361 lua_game_control &= ~(LGC_NORMAL|LGC_FULL); 362 return ade_set_args(L, "s", "LUA STEERING CONTROLS"); 363 case LE_LUA_FULL_CONTROLS: 364 lua_game_control |= LGC_FULL; 365 lua_game_control &= ~(LGC_STEERING|LGC_NORMAL); 366 return ade_set_args(L, "s", "LUA FULL CONTROLS"); 367 default: 368 return ade_set_error(L, "s", ""); 369 } 370 } 371 372 ADE_FUNC(setButtonControlMode, 373 l_Base, 374 "nil|enumeration mode /* LE_*_BUTTON_CONTROL */", 375 "Sets the current control mode for the game.", 376 "string", 377 "Current control mode") 378 { 379 enum_h *e = NULL; 380 if (!(ade_get_args(L, "|o", l_Enum.GetPtr(&e)))) { 381 if (lua_game_control & LGC_B_NORMAL) 382 return ade_set_args(L, "s", "NORMAL"); 383 else if (lua_game_control & LGC_B_OVERRIDE) 384 return ade_set_args(L, "s", "OVERRIDE"); 385 else if (lua_game_control & LGC_B_ADDITIVE) 386 return ade_set_args(L, "s", "ADDITIVE"); 387 else 388 return ade_set_error(L, "s", ""); 389 } 390 391 if (!e) { 392 return ade_set_error(L, "s", ""); 393 } 394 395 switch (e->index) { 396 case LE_NORMAL_BUTTON_CONTROLS: 397 lua_game_control |= LGC_B_NORMAL; 398 lua_game_control &= ~(LGC_B_ADDITIVE|LGC_B_OVERRIDE); 399 return ade_set_args(L, "s", "NORMAL BUTTON CONTROL"); 400 case LE_LUA_ADDITIVE_BUTTON_CONTROL: 401 lua_game_control |= LGC_B_ADDITIVE; 402 lua_game_control &= ~(LGC_B_NORMAL|LGC_B_OVERRIDE); 403 return ade_set_args(L, "s", "LUA ADDITIVE BUTTON CONTROL"); 404 case LE_LUA_OVERRIDE_BUTTON_CONTROL: 405 lua_game_control |= LGC_B_OVERRIDE; 406 lua_game_control &= ~(LGC_B_ADDITIVE|LGC_B_NORMAL); 407 return ade_set_args(L, "s", "LUA OVERRIDE BUTTON CONTROL"); 408 default: 409 return ade_set_error(L, "s", ""); 410 } 411 } 412 413 ADE_FUNC(getControlInfo, l_Base, nullptr, "Gets the control info handle.", "control_info", "control info handle") 414 { 415 return ade_set_args(L, "o", l_Control_Info.Set(1)); 416 } 417 418 ADE_FUNC(setTips, l_Base, "boolean", "Sets whether to display tips of the day the next time the current pilot enters the mainhall.", nullptr, nullptr) 419 { 420 if (Player == NULL) 421 return ADE_RETURN_NIL; 422 423 bool tips = false; 424 425 ade_get_args(L, "b", &tips); 426 427 if (tips) 428 Player->tips = 1; 429 else 430 Player->tips = 0; 431 432 return ADE_RETURN_NIL; 433 } 434 435 ADE_FUNC(getGameDifficulty, l_Base, nullptr, 436 "Returns the difficulty level from 1-5, 1 being the lowest, (Very Easy) and 5 being the highest (Insane)", 437 "number", "Difficulty level as integer") 438 { 439 return ade_set_args(L, "i", Game_skill_level+1); 440 } 441 442 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") 443 { 444 gameevent_h *gh = NULL; 445 if(!ade_get_args(L, "o", l_GameEvent.GetPtr(&gh))) 446 return ade_set_error(L, "b", false); 447 448 if(!gh->IsValid()) 449 return ade_set_error(L, "b", false); 450 451 gameseq_post_event(gh->Get()); 452 453 return ADE_RETURN_TRUE; 454 } 455 456 ADE_FUNC(XSTR, 457 l_Base, 458 "string text, number id", 459 "Gets the translated version of text with the given id. " 460 "The uses the tstrings table for performing the translation. Passing -1 as the id will always return the given text.", 461 "string", 462 "The translated text") { 463 const char* text = nullptr; 464 int id = -1; 465 466 if (!ade_get_args(L, "si", &text, &id)) { 467 return ADE_RETURN_NIL; 468 } 469 470 SCP_string xstr; 471 sprintf(xstr, "XSTR(\"%s\", %d)", text, id); 472 473 SCP_string translated; 474 lcl_ext_localize(xstr, translated); 475 476 return ade_set_args(L, "s", translated.c_str()); 477 } 478 479 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") { 480 return ade_set_args(L, "b", Fred_running != 0); 481 } 482 483 ADE_FUNC(isEngineVersionAtLeast, 484 l_Base, 485 "number major, number minor, number build, [number revision = 0]", 486 "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.", 487 "boolean", 488 "true if the version is at least the specified version. false otherwise.") { 489 int major = 0; 490 int minor = 0; 491 int build = 0; 492 int revision = 0; 493 494 if (!ade_get_args(L, "iii|i", &major, &minor, &build, &revision)) { 495 return ade_set_error(L, "b", false); 496 } 497 498 auto version = gameversion::version(major, minor, build, revision); 499 500 return ade_set_args(L, "b", gameversion::check_at_least(version)); 501 } 502 503 ADE_FUNC(getCurrentLanguage, 504 l_Base, 505 nullptr, 506 "Determines the language that is being used by the engine. This returns the full name of the language (e.g. \"English\").", 507 "string", 508 "The current game language") 509 { 510 const char *lang_name; 511 if (Lcl_current_lang == LCL_UNTRANSLATED) 512 lang_name = "UNTRANSLATED"; 513 else if (Lcl_current_lang == LCL_RETAIL_HYBRID) 514 lang_name = "RETAIL HYBRID"; 515 else 516 lang_name = Lcl_languages[lcl_get_current_lang_index()].lang_name; 517 518 return ade_set_args(L, "s", lang_name); 519 } 520 521 ADE_FUNC(getCurrentLanguageExtension, 522 l_Base, 523 nullptr, 524 "Determines the file extension of the language that is being used by the engine. " 525 "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). " 526 "This will return an empty string for the default language.", 527 "string", 528 "The current game language") 529 { 530 int lang = lcl_get_current_lang_index(); 531 return ade_set_args(L, "s", Lcl_languages[lang].lang_ext); 532 } 533 534 ADE_FUNC(getVersionString, l_Base, nullptr, 535 "Returns a string describing the version of the build that is currently running. This is mostly intended to " 536 "be displayed to the user and not processed by a script so don't rely on the exact format of the string.", 537 "string", "The version information") 538 { 539 auto str = gameversion::get_version_string(); 540 return ade_set_args(L, "s", str.c_str()); 541 } 542 543 ADE_VIRTVAR(MultiplayerMode, l_Base, "boolean", "Determines if the game is currently in single- or multiplayer mode", 544 "boolean", 545 "true if in multiplayer mode, false if in singleplayer. If neither is the case (e.g. on game init) nil " 546 "will be returned") 547 { 548 bool b; 549 if (!ade_get_args(L, "*|b", &b)) { 550 return ADE_RETURN_NIL; 551 } 552 553 if (ADE_SETTING_VAR) { 554 if (b) { 555 Game_mode &= ~GM_NORMAL; 556 Game_mode |= GM_MULTIPLAYER; 557 } else { 558 Game_mode &= ~GM_MULTIPLAYER; 559 Game_mode |= GM_NORMAL; 560 } 561 } 562 563 if (Game_mode & GM_MULTIPLAYER) { 564 return ADE_RETURN_TRUE; 565 } else if (Game_mode & GM_NORMAL) { 566 return ADE_RETURN_FALSE; 567 } else { 568 return ADE_RETURN_NIL; 569 } 570 } 571 572 ADE_FUNC(serializeValue, 573 l_Base, 574 "any value", 575 "Serializes the specified value so that it can be stored and restored consistently later. The actual format of the " 576 "returned data is implementation specific but will be deserializable by at least this engine version and following " 577 "versions.", 578 "bytearray", 579 "The serialized representation of the value or nil on error.") 580 { 581 luacpp::LuaValue value; 582 if (!ade_get_args(L, "a", &value)) { 583 return ADE_RETURN_NIL; 584 } 585 586 try { 587 util::LuaValueSerializer serializer(std::move(value)); 588 auto serialized = serializer.serialize(); 589 590 return ade_set_args(L, "o", l_Bytearray.Set(bytearray_h(std::move(serialized)))); 591 } catch (const std::exception& e) { 592 LuaError(L, "Failed to serialize value: %s", e.what()); 593 return ADE_RETURN_NIL; 594 } 595 } 596 597 ADE_FUNC(deserializeValue, 598 l_Base, 599 "bytearray serialized", 600 "Deserializes a previously serialized Lua value.", 601 "any", 602 "The deserialized Lua value.") 603 { 604 bytearray_h* array = nullptr; 605 if (!ade_get_args(L, "o", l_Bytearray.GetPtr(&array))) { 606 return ade_set_args(L, "o", l_Bytearray.Set(bytearray_h())); 607 } 608 609 try { 610 util::LuaValueDeserializer deserializer(L); 611 auto deserialized = deserializer.deserialize(array->data()); 612 613 return ade_set_args(L, "a", deserialized); 614 } catch (const std::exception& e) { 615 LuaError(L, "Failed to deserialize value: %s", e.what()); 616 return ADE_RETURN_NIL; 617 } 618 } 619 620 //**********SUBLIBRARY: Base/Events 621 ADE_LIB_DERIV(l_Base_Events, "GameEvents", NULL, "Freespace 2 game events", l_Base); 622 623 ADE_INDEXER(l_Base_Events, "number/string IndexOrName", "Array of game events", "gameevent", "Game event, or invalid gameevent handle if index is invalid") 624 { 625 const char* name; 626 if(!ade_get_args(L, "*s", &name)) 627 return ade_set_error(L, "o", l_GameEvent.Set(gameevent_h())); 628 629 int idx = gameseq_get_event_idx(name); 630 631 if(idx < 0) 632 { 633 idx = atoi(name); 634 635 //Lua-->FS2 636 idx--; 637 638 if(idx < 0 || idx >= Num_gs_event_text) 639 return ade_set_error(L, "o", l_GameEvent.Set(gameevent_h())); 640 } 641 642 return ade_set_args(L, "o", l_GameEvent.Set(gameevent_h(idx))); 643 } 644 645 ADE_FUNC(__len, l_Base_Events, NULL, "Number of events", "number", "Number of events") 646 { 647 return ade_set_args(L, "i", Num_gs_event_text); 648 } 649 650 //**********SUBLIBRARY: Base/States 651 ADE_LIB_DERIV(l_Base_States, "GameStates", NULL, "Freespace 2 states", l_Base); 652 653 ADE_INDEXER(l_Base_States, "number/string IndexOrName", "Array of game states", "gamestate", "Game state, or invalid gamestate handle if index is invalid") 654 { 655 const char* name; 656 if(!ade_get_args(L, "*s", &name)) 657 return ade_set_error(L, "o", l_GameState.Set(gamestate_h())); 658 659 int idx = gameseq_get_state_idx(name); 660 661 if(idx < 0) 662 { 663 idx = atoi(name); 664 665 //Lua-->FS2 666 idx--; 667 668 if(idx < 0 || idx >= Num_gs_state_text) 669 return ade_set_error(L, "o", l_GameState.Set(gamestate_h())); 670 } 671 672 return ade_set_args(L, "o", l_GameState.Set(gamestate_h(idx))); 673 } 674 675 ADE_FUNC(__len, l_Base_States, NULL, "Number of states", "number", "Number of states") 676 { 677 return ade_set_args(L, "i", Num_gs_state_text); 678 } 679 680 681 } 682 } 683 684