diff --git a/code/scripting/api/objs/ship.cpp b/code/scripting/api/objs/ship.cpp index 7f86a03cfaa..c9ba7d1fd52 100644 --- a/code/scripting/api/objs/ship.cpp +++ b/code/scripting/api/objs/ship.cpp @@ -36,6 +36,9 @@ #include "ship/shipfx.h" #include "ship/shiphit.h" +#include "scripting/lua/LuaException.h" +#include "scripting/lua/LuaUtil.h" + extern void ship_reset_disabled_physics(object *objp, int ship_class); extern bool sexp_check_flag_arrays(const char *flag_name, Object::Object_Flags &object_flag, Ship::Ship_Flags &ship_flags, Mission::Parse_Object_Flags &parse_obj_flag, AI::AI_Flags &ai_flag); extern void sexp_alter_ship_flag_helper(object_ship_wing_point_team &oswpt, bool future_ships, Object::Object_Flags object_flag, Ship::Ship_Flags ship_flag, Mission::Parse_Object_Flags parse_obj_flag, AI::AI_Flags ai_flag, bool set_flag); @@ -2810,5 +2813,68 @@ ADE_FUNC(ModifyElectricArc, l_Ship, "number index, vector firstPoint, vector sec return ADE_RETURN_NIL; } +ADE_FUNC(ModifyElectricArcPoints, l_Ship, "number index, table points, [number width]", + "Sets the collection of persistent points to be used by this arc, as well as optionally the arc's width. " + "The table of points should consist of Vectors (e.g. created with ba.createVector()), arrays with three elements each, or tables with 'x'/'X', 'y'/'Y', and 'z'/'Z' pairs. There must be at least two points.", + nullptr, + nullptr) +{ + object_h* objh = nullptr; + int index; + luacpp::LuaTable luaPoints; + float width = 0.0f; + + int args = ade_get_args(L, "oit|f", l_Ship.GetPtr(&objh), &index, &luaPoints, &width); + if (args < 3) + return ADE_RETURN_NIL; + + if (!objh->isValid()) + return ADE_RETURN_NIL; + + auto shipp = &Ships[objh->objp()->instance]; + + index--; // Lua -> FS2 + if (SCP_vector_inbounds(shipp->electrical_arcs, index) && luaPoints.isValid()) + { + SCP_vector fsoPoints; + + // convert Lua points to FSO points + for (const auto &entry : luaPoints) + { + try + { + fsoPoints.push_back(luacpp::util::valueToVec3d(entry.second)); + } + catch (const luacpp::LuaException &e) + { + LuaError(L, "%s", e.what()); + return ADE_RETURN_NIL; + } + } + + if (fsoPoints.size() < 2) + { + LuaError(L, "Points table passed to ship:ModifyElectricArcPoints() has fewer than two points!"); + return ADE_RETURN_NIL; + } + + auto &arc = shipp->electrical_arcs[index]; + arc.endpoint_1 = fsoPoints.front(); + arc.endpoint_2 = fsoPoints.back(); + + // need to create the persistent point storage if it isn't set up yet + if (!arc.persistent_arc_points) + arc.persistent_arc_points.reset(new SCP_vector()); + + // assign all of our new points to the persistent point storage + arc.persistent_arc_points->operator=(std::move(fsoPoints)); + + if (args >= 4) + arc.width = width; + } + + return ADE_RETURN_NIL; +} + } } diff --git a/code/scripting/lua/LuaUtil.cpp b/code/scripting/lua/LuaUtil.cpp index 043cae53f67..1015a1e879c 100644 --- a/code/scripting/lua/LuaUtil.cpp +++ b/code/scripting/lua/LuaUtil.cpp @@ -1,6 +1,8 @@ #include "LuaUtil.h" +#include "scripting/api/objs/vecmath.h" + namespace { const char* mainStateRefName = "_lua_mainthread"; } @@ -62,5 +64,81 @@ lua_State* getMainThread(lua_State* L) return state; } +vec3d valueToVec3d(const LuaValue &luaValue) +{ + if (luaValue.is(luacpp::ValueType::TABLE)) + { + vec3d tablePoint = vmd_zero_vector; + int index = 0; + bool is_array = false; + bool is_table = false; + + // we have to go deeper + luacpp::LuaTable childTable; + childTable.setReference(luaValue.getReference()); + for (const auto &childEntry : childTable) + { + // note: this is pre-increment + if (index >= 3) + throw LuaException("Vec3d table has more than three entries!"); + + if (!childEntry.second.is(luacpp::ValueType::NUMBER)) + throw LuaException("Value in vec3d table is not a number!"); + + auto number = static_cast(childEntry.second.getValue()); + + if (childEntry.first.is(luacpp::ValueType::STRING)) + { + is_table = true; + if (is_array) + throw LuaException("Vec3d table has an unexpected format!"); + + const auto &str = childEntry.first.getValue(); + if (lcase_equal(str, "x")) + tablePoint.xyz.x = number; + else if (lcase_equal(str, "y")) + tablePoint.xyz.y = number; + else if (lcase_equal(str, "z")) + tablePoint.xyz.z = number; + else + throw LuaException("Vec3d table has an entry other than x/X, y/Y, or z/Z!"); + } + else + { + is_array = true; + if (is_table) + throw LuaException("Vec3d table has an unexpected format!"); + + tablePoint.a1d[index] = number; + } + + index++; + } + + // note: this is post-increment + if (index < 3) + throw LuaException("Vec3d table has fewer than three entries!"); + + return tablePoint; + } + else if (luaValue.is(luacpp::ValueType::USERDATA)) + { + try + { + vec3d v; + luaValue.getValue(scripting::api::l_Vector.Get(&v)); + return v; + } + catch (const luacpp::LuaException& /*e*/) + { + throw LuaException("Userdata in vec3d table is not a vector!"); + } + } + else + { + throw LuaException("Vec3d value is of an unhandled type!"); + } +} + } // namespace util } // namespace luacpp diff --git a/code/scripting/lua/LuaUtil.h b/code/scripting/lua/LuaUtil.h index 89b20c4e8f4..9b5af679f4e 100644 --- a/code/scripting/lua/LuaUtil.h +++ b/code/scripting/lua/LuaUtil.h @@ -91,6 +91,14 @@ void tableToList(LuaTable& table, Container& list) { } const char* getValueName(ValueType type); + +/** + * Processes a LuaValue and returns a vec3d. The LuaValue could be a Vector, an array with three elements, or a table with x/X, y/Y, and z/Z entries. + * + * Throws a LuaException if the conversion was not successful. + */ +vec3d valueToVec3d(const LuaValue &luaValue); + } }