Files
fs2open.github.com/code/scripting/api/objs/propclass.cpp
T
Mike NelsonandAsteroth 070a8e18fa Adds Props as a new object type (#6836)
* First draft of prop object type

* Add prop editor dialog in FRED

* prop sexps

* lua table prop support

* lab support

* lua mission access to props

* prop collision support

* use std::optional so the object->instance id is always valid

* allow props to block suns

* minor cleanup and remove arbitrary limit. Still limited to 500 objects.

* Sort props by category

* cleanup

* implement collision hooks

* parse prop flags and further ensure props are inited and closed correctly

* Add Cyborg's multi suggestions

* fix missing header

* add props to empty slots if available

* add multi signature

* fix rebase issues and match collisions to the new multithread style

* appease Clang the Conquerer

* save/parse mission prop classes by name

* Clean up comments and finish TODOs

* fix string errors

* clang wants this to be static now

* address feedback

* address feedback

* Use model num from prop info instead

* merge prop and ship collision group sexps together

* fix rebase bug

* address feedback

* fix name shadowing

* ship heavy submodel

* clang

---------

Co-authored-by: Asteroth <[email protected]>
2026-02-16 14:41:34 +01:00

340 lines
8.1 KiB
C++

#include "propclass.h"
#include "model.h"
#include "vecmath.h"
#include "prop/prop.h"
namespace scripting::api {
//**********HANDLE: Propclass
ADE_OBJ(l_Propclass, int, "propclass", "Prop class handle");
ADE_FUNC(__tostring,
l_Propclass,
nullptr,
"Prop class name",
"string",
"Prop class name, or an empty string if handle is invalid")
{
int idx;
const char* s = nullptr;
if (!ade_get_args(L, "o|s", l_Propclass.Get(&idx), &s))
return ade_set_error(L, "s", "");
if (idx < 0 || idx >= prop_info_size())
return ade_set_error(L, "s", "");
return ade_set_args(L, "s", Prop_info[idx].name);
}
ADE_FUNC(__eq,
l_Propclass,
"propclass, propclass",
"Checks if the two classes are equal",
"boolean",
"true if equal, false otherwise")
{
int idx1, idx2;
if (!ade_get_args(L, "oo", l_Propclass.Get(&idx1), l_Propclass.Get(&idx2)))
return ade_set_error(L, "b", false);
if (idx1 < 0 || idx1 >= prop_info_size())
return ade_set_error(L, "b", false);
if (idx2 < 0 || idx2 >= prop_info_size())
return ade_set_error(L, "b", false);
return ade_set_args(L, "b", idx1 == idx2);
}
ADE_VIRTVAR(Name,
l_Propclass,
"string",
"Prop class name",
"string",
"Prop class name, or an empty string if handle is invalid")
{
int idx;
const char* s = nullptr;
if (!ade_get_args(L, "o|s", l_Propclass.Get(&idx), &s))
return ade_set_error(L, "s", "");
if (idx < 0 || idx >= prop_info_size())
return ade_set_error(L, "s", "");
if (ADE_SETTING_VAR) {
LuaError(L, "Setting prop class name is not supported");
}
return ade_set_args(L, "s", Prop_info[idx].name);
}
ADE_VIRTVAR(Model,
l_Propclass,
"model",
"Model",
"model",
"Prop class model, or invalid model handle if propclass handle is invalid")
{
int idx = -1;
model_h* mdl = nullptr;
if (!ade_get_args(L, "o|o", l_Propclass.Get(&idx), l_Model.GetPtr(&mdl)))
return ade_set_error(L, "o", l_Model.Set(model_h(-1)));
if (idx < 0 || idx >= prop_info_size())
return ade_set_error(L, "o", l_Model.Set(model_h(-1)));
prop_info* pip = &Prop_info[idx];
if (ADE_SETTING_VAR) {
LuaError(L, "Setting prop class model is not supported");
}
return ade_set_args(L, "o", l_Model.Set(model_h(pip->model_num)));
}
ADE_VIRTVAR(CustomData,
l_Propclass,
nullptr,
"Gets the custom data table for this prop class",
"table",
"The prop class's custom data table")
{
int idx;
if (!ade_get_args(L, "o", l_Propclass.Get(&idx)))
return ADE_RETURN_NIL;
if (idx < 0 || idx >= prop_info_size())
return ADE_RETURN_NIL;
auto table = luacpp::LuaTable::create(L);
prop_info* pip = &Prop_info[idx];
for (const auto& pair : pip->custom_data) {
table.addValue(pair.first, pair.second);
}
return ade_set_args(L, "t", &table);
}
ADE_FUNC(hasCustomData,
l_Propclass,
nullptr,
"Detects whether the prop class has any custom data",
"boolean",
"true if the propclass's custom_data is not empty, false otherwise")
{
int idx;
if (!ade_get_args(L, "o", l_Propclass.Get(&idx)))
return ADE_RETURN_NIL;
if (idx < 0 || idx >= prop_info_size())
return ADE_RETURN_NIL;
prop_info* pip = &Prop_info[idx];
bool result = !pip->custom_data.empty();
return ade_set_args(L, "b", result);
}
ADE_VIRTVAR(CustomStrings,
l_Propclass,
nullptr,
"Gets the indexed custom string table for this prop. Each item in the table is a table with the following values: "
"Name - the name of the custom string, Value - the value associated with the custom string, String - the custom "
"string itself.",
"table",
"The prop's custom data table")
{
int idx;
if (!ade_get_args(L, "o", l_Propclass.Get(&idx)))
return ADE_RETURN_NIL;
if (idx < 0 || idx >= prop_info_size())
return ADE_RETURN_NIL;
prop_info* pip = &Prop_info[idx];
if (ADE_SETTING_VAR) {
LuaError(L, "Setting Custom Data is not supported");
}
auto table = luacpp::LuaTable::create(L);
int cnt = 0;
for (const auto& cs : pip->custom_strings) {
cnt++;
auto item = luacpp::LuaTable::create(L);
item.addValue("Name", luacpp::LuaValue::createValue(Script_system.GetLuaSession(), cs.name));
item.addValue("Value", luacpp::LuaValue::createValue(Script_system.GetLuaSession(), cs.value));
item.addValue("String", luacpp::LuaValue::createValue(Script_system.GetLuaSession(), cs.text));
table.addValue(cnt, item);
}
return ade_set_args(L, "t", &table);
}
ADE_FUNC(hasCustomStrings,
l_Propclass,
nullptr,
"Detects whether the prop has any custom strings",
"boolean",
"true if the prop's custom_strings is not empty, false otherwise")
{
int idx;
if (!ade_get_args(L, "o", l_Propclass.Get(&idx)))
return ADE_RETURN_NIL;
if (idx < 0 || idx >= prop_info_size())
return ADE_RETURN_NIL;
prop_info* pip = &Prop_info[idx];
bool result = !pip->custom_strings.empty();
return ade_set_args(L, "b", result);
}
ADE_FUNC(isValid,
l_Propclass,
nullptr,
"Detects whether handle is valid",
"boolean",
"true if valid, false if handle is invalid, nil if a syntax/type error occurs")
{
int idx;
if (!ade_get_args(L, "o", l_Propclass.Get(&idx)))
return ADE_RETURN_NIL;
if (idx < 0 || idx >= prop_info_size())
return ADE_RETURN_FALSE;
return ADE_RETURN_TRUE;
}
ADE_FUNC(renderTechModel,
l_Propclass,
"number X1, number Y1, number X2, number Y2, [number RotationPercent =0, number PitchPercent =0, number "
"BankPercent=40, number Zoom=1.3, boolean Lighting=true]",
"Draws prop model as if in techroom. True for regular lighting, false for flat lighting.",
"boolean",
"Whether prop was rendered")
{
int x1, y1, x2, y2;
angles rot_angles = {0.0f, 0.0f, 40.0f};
int idx;
float zoom = 1.3f;
bool lighting = true;
if (!ade_get_args(L,
"oiiii|ffffb",
l_Propclass.Get(&idx),
&x1,
&y1,
&x2,
&y2,
&rot_angles.h,
&rot_angles.p,
&rot_angles.b,
&zoom,
&lighting))
return ade_set_error(L, "b", false);
if (idx < 0 || idx >= prop_info_size())
return ade_set_args(L, "b", false);
if (x2 < x1 || y2 < y1)
return ade_set_args(L, "b", false);
CLAMP(rot_angles.p, 0.0f, 100.0f);
CLAMP(rot_angles.b, 0.0f, 100.0f);
CLAMP(rot_angles.h, 0.0f, 100.0f);
// Handle angles
matrix orient = vmd_identity_matrix;
angles view_angles = {-0.6f, 0.0f, 0.0f};
vm_angles_2_matrix(&orient, &view_angles);
rot_angles.p = (rot_angles.p * 0.01f) * PI2;
rot_angles.b = (rot_angles.b * 0.01f) * PI2;
rot_angles.h = (rot_angles.h * 0.01f) * PI2;
vm_rotate_matrix_by_angles(&orient, &rot_angles);
return ade_set_args(L, "b", render_tech_model(TECH_PROP, x1, y1, x2, y2, zoom, lighting, idx, &orient));
}
// Nuke's alternate tech model rendering function
ADE_FUNC(renderTechModel2,
l_Propclass,
"number X1, number Y1, number X2, number Y2, [orientation Orientation=nil, number Zoom=1.3]",
"Draws prop model as if in techroom",
"boolean",
"Whether prop was rendered")
{
int x1, y1, x2, y2;
int idx;
float zoom = 1.3f;
matrix_h* mh = nullptr;
if (!ade_get_args(L, "oiiiio|f", l_Propclass.Get(&idx), &x1, &y1, &x2, &y2, l_Matrix.GetPtr(&mh), &zoom))
return ade_set_error(L, "b", false);
if (idx < 0 || idx >= prop_info_size())
return ade_set_args(L, "b", false);
if (x2 < x1 || y2 < y1)
return ade_set_args(L, "b", false);
// Handle angles
matrix* orient = mh->GetMatrix();
return ade_set_args(L, "b", render_tech_model(TECH_PROP, x1, y1, x2, y2, zoom, true, idx, orient));
}
ADE_FUNC(isModelLoaded,
l_Propclass,
"[boolean Load = false]",
"Checks if the model used for this propclass is loaded or not and optionally loads the model, which might be a "
"slow operation.",
"boolean",
"If the model is loaded or not")
{
int idx;
bool load_check = false;
if (!ade_get_args(L, "o|b", l_Propclass.Get(&idx), &load_check))
return ADE_RETURN_FALSE;
prop_info* pip = &Prop_info[idx];
if (pip == nullptr)
return ADE_RETURN_FALSE;
if (load_check) {
pip->model_num = model_load(pip->pof_file.c_str());
}
if (pip->model_num > -1)
return ADE_RETURN_TRUE;
else
return ADE_RETURN_FALSE;
}
ADE_FUNC(getPropClassIndex,
l_Propclass,
nullptr,
"Gets the index value of the prop class",
"number",
"index value of the prop class")
{
int idx;
if (!ade_get_args(L, "o", l_Propclass.Get(&idx)))
return ade_set_args(L, "i", -1);
if (idx < 0 || idx >= prop_info_size())
return ade_set_args(L, "i", -1);
return ade_set_args(L, "i", idx + 1); // Lua is 1-based
}
} // namespace scripting::api