Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e300c26
First draft of prop object type
MjnMixael Jun 2, 2025
25773f2
Add prop editor dialog in FRED
MjnMixael Jun 11, 2025
ecb5356
prop sexps
MjnMixael Jun 12, 2025
8bc3251
lua table prop support
MjnMixael Jun 12, 2025
33be67b
lab support
MjnMixael Jun 12, 2025
6cacbba
lua mission access to props
MjnMixael Jun 13, 2025
d4af6da
prop collision support
Baezon Jun 14, 2025
3f62616
use std::optional so the object->instance id is always valid
MjnMixael Jun 14, 2025
bf2a3aa
allow props to block suns
MjnMixael Jun 15, 2025
224b367
minor cleanup and remove arbitrary limit. Still limited to 500 objects.
MjnMixael Jun 15, 2025
95e0883
Sort props by category
MjnMixael Jun 16, 2025
b195a19
cleanup
MjnMixael Jun 16, 2025
a7562f2
implement collision hooks
MjnMixael Jun 16, 2025
592d8d8
parse prop flags and further ensure props are inited and closed corre…
MjnMixael Jun 17, 2025
084f3c9
Add Cyborg's multi suggestions
MjnMixael Jun 17, 2025
2ddfe50
fix missing header
MjnMixael Jun 17, 2025
57b532b
add props to empty slots if available
MjnMixael Jun 17, 2025
db49e23
add multi signature
MjnMixael Jun 18, 2025
8434f84
fix rebase issues and match collisions to the new multithread style
MjnMixael Jul 14, 2025
cdcba90
appease Clang the Conquerer
MjnMixael Jul 14, 2025
812a9bd
save/parse mission prop classes by name
MjnMixael Jul 30, 2025
d134321
Clean up comments and finish TODOs
MjnMixael Jul 30, 2025
36e5032
fix string errors
MjnMixael Jul 30, 2025
6f11853
clang wants this to be static now
MjnMixael Jul 30, 2025
f468387
address feedback
MjnMixael Oct 17, 2025
b0f402d
address feedback
MjnMixael Jan 30, 2026
ea6427b
Use model num from prop info instead
MjnMixael Feb 10, 2026
794a994
merge prop and ship collision group sexps together
MjnMixael Feb 11, 2026
9262546
fix rebase bug
MjnMixael Feb 15, 2026
4580726
address feedback
MjnMixael Feb 15, 2026
e9bb0d6
fix name shadowing
MjnMixael Feb 15, 2026
209d0af
ship heavy submodel
MjnMixael Feb 15, 2026
f437665
clang
MjnMixael Feb 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions code/graphics/shadows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ void shadows_render_all(fov_t fov, matrix *eye_orient, vec3d *eye_pos)
switch(objp->type)
{
case OBJ_RAW_POF:
case OBJ_PROP:
case OBJ_SHIP:
{
obj_queue_render(objp, &scene);
Expand Down
68 changes: 66 additions & 2 deletions code/lab/dialogs/lab_ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "ship/shiphit.h"
#include "weapon/weapon.h"
#include "mission/missionload.h"
#include "prop/prop.h"

using namespace ImGui;

Expand Down Expand Up @@ -92,6 +93,32 @@ void LabUi::build_weapon_subtype_list() const
}
}

void LabUi::build_prop_subtype_list()
{
for (auto& propc : Prop_categories) {
with_TreeNode(propc.name.c_str())
{
int prop_idx = 0;

for (auto const& class_def : Prop_info) {
if (lcase_equal(prop_get_category(class_def.category_index)->name, propc.name)) {
SCP_string node_label;
sprintf(node_label, "##PropClassIndex%i", prop_idx);
TreeNodeEx(node_label.c_str(),
ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen,
"%s",
class_def.name.c_str());

if (IsItemClicked() && !IsItemToggledOpen()) {
getLabManager()->changeDisplayedObject(LabMode::Prop, prop_idx);
}
}
prop_idx++;
}
}
}
}

void LabUi::build_asteroid_list()
{
with_TreeNode("Asteroids")
Expand Down Expand Up @@ -171,7 +198,15 @@ void LabUi::build_object_list()
}
}

void LabUi::build_background_list() const
void LabUi::build_prop_list()
{
with_TreeNode("Prop Classes")
{
build_prop_subtype_list();
}
}

void LabUi::build_background_list()
{
SCP_vector<SCP_string> t_missions;

Expand Down Expand Up @@ -272,6 +307,8 @@ void LabUi::show_object_selector() const

build_weapon_list();

build_prop_list();

build_object_list();
}
}
Expand Down Expand Up @@ -418,7 +455,8 @@ void LabUi::show_render_options()
Checkbox("Rotate/Translate Subsystems", &animate_subsystems);
}
Checkbox("Show full detail", &show_full_detail);
if (getLabManager()->CurrentMode != LabMode::Asteroid) {
if (getLabManager()->CurrentMode == LabMode::Ship ||
getLabManager()->CurrentMode == LabMode::Weapon) {
Checkbox("Show thrusters", &show_thrusters);
if (getLabManager()->CurrentMode == LabMode::Ship) {
Checkbox("Show afterburners", &show_afterburners);
Expand Down Expand Up @@ -1485,6 +1523,32 @@ void LabUi::show_object_options() const
}
}
}
} else if (getLabManager()->CurrentMode == LabMode::Prop && getLabManager()->CurrentClass >= 0) {
const auto& info = Prop_info[getLabManager()->CurrentClass];

with_CollapsingHeader("Object Info")
{
static SCP_string table_text;
static int old_class = -1;

if (table_text.empty() || old_class != getLabManager()->CurrentClass) {
table_text = get_prop_table_text(&info);
old_class = getLabManager()->CurrentClass;
}

InputTextMultiline("##prop_table_text",
const_cast<char*>(table_text.c_str()),
table_text.length(),
ImVec2(-FLT_MIN, GetTextLineHeight() * 16),
ImGuiInputTextFlags_ReadOnly);
}

with_CollapsingHeader("Object actions")
{
if (getLabManager()->isSafeForProps()) {
// No actions yet
}
}
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion code/lab/dialogs/lab_ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ class LabUi {
static void build_object_list();
static void build_asteroid_list();
static void build_debris_list();
void build_background_list() const;
static void build_prop_list();
static void build_prop_subtype_list();
static void build_background_list();
void show_render_options();
void show_object_options() const;
void show_object_selector() const;
Expand Down
122 changes: 122 additions & 0 deletions code/lab/dialogs/lab_ui_helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,128 @@ SCP_string get_asteroid_table_text(const asteroid_info* aip)
return result;
}

SCP_string get_prop_table_text(const prop_info* pip)
{
char line[256], line2[256], file_text[82];
int i, j, n, found = 0, comment = 0, num_files = 0;
Comment thread
BMagnu marked this conversation as resolved.
SCP_vector<SCP_string> tbl_file_names;
SCP_string result;

auto fp = cfopen("props.tbl", "r");
if (!fp)
return "No props.tbl found.\r\n";

while (cfgets(line, 255, fp)) {
while (line[strlen(line) - 1] == '\n')
line[strlen(line) - 1] = 0;

for (i = j = 0; line[i]; i++) {
if (line[i] == '/' && line[i + 1] == '/')
break;
if (line[i] == '/' && line[i + 1] == '*') {
comment = 1;
i++;
continue;
}
if (line[i] == '*' && line[i + 1] == '/') {
comment = 0;
i++;
continue;
}
if (!comment)
line2[j++] = line[i];
}

line2[j] = 0;
if (!strnicmp(line2, "$Name:", 6)) {
drop_trailing_white_space(line2);
found = 0;
i = 6;

while (line2[i] == ' ' || line2[i] == '\t' || line2[i] == '@')
i++;

if (!stricmp(line2 + i, pip->name.c_str())) {
result += "-- props.tbl -------------------------------\r\n";
found = 1;
}
}

if (found) {
result += line;
result += "\r\n";
}
}

cfclose(fp);

num_files = cf_get_file_list(tbl_file_names, CF_TYPE_TABLES, NOX("*-prp.tbm"), CF_SORT_REVERSE);

for (n = 0; n < num_files; n++) {
tbl_file_names[n] += ".tbm";

fp = cfopen(tbl_file_names[n].c_str(), "r");
if (!fp)
continue;

memset(line, 0, sizeof(line));
memset(line2, 0, sizeof(line2));
found = 0;
comment = 0;

while (cfgets(line, 255, fp)) {
while (line[strlen(line) - 1] == '\n')
line[strlen(line) - 1] = 0;

for (i = j = 0; line[i]; i++) {
if (line[i] == '/' && line[i + 1] == '/')
break;
if (line[i] == '/' && line[i + 1] == '*') {
comment = 1;
i++;
continue;
}
if (line[i] == '*' && line[i + 1] == '/') {
comment = 0;
i++;
continue;
}
if (!comment)
line2[j++] = line[i];
}

line2[j] = 0;
if (!strnicmp(line2, "$Name:", 6)) {
drop_trailing_white_space(line2);
found = 0;
i = 6;

while (line2[i] == ' ' || line2[i] == '\t' || line2[i] == '@')
i++;

if (!stricmp(line2 + i, pip->name.c_str())) {
memset(file_text, 0, sizeof(file_text));
snprintf(file_text,
sizeof(file_text) - 1,
"-- %s -------------------------------\r\n",
tbl_file_names[n].c_str());
result += file_text;
found = 1;
}
}

if (found) {
result += line;
result += "\r\n";
}
}

cfclose(fp);
}

return result;
}

SCP_string get_directory_or_vp(const char* path)
{
SCP_string result(path);
Expand Down
3 changes: 3 additions & 0 deletions code/lab/dialogs/lab_ui_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "asteroid/asteroid.h"
#include "ship/ship.h"
#include "prop/prop.h"

SCP_map<int, SCP_string> get_docking_point_map(int model_index);

Expand All @@ -13,6 +14,8 @@ SCP_string get_weapon_table_text(weapon_info* wip);

SCP_string get_asteroid_table_text(const asteroid_info* aip);

SCP_string get_prop_table_text(const prop_info* pip);

SCP_string get_directory_or_vp(const char* path);

bool graphics_options_changed();
1 change: 1 addition & 0 deletions code/lab/labv2.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ enum class LabMode {
Asteroid,
Ship,
Weapon,
Prop,
None
};

Expand Down
8 changes: 8 additions & 0 deletions code/lab/manager/lab_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "ship/ship.h"
#include "ship/shipfx.h"
#include "particle/particle.h"
#include "prop/prop.h"
#include "weapon/muzzleflash.h"
#include "weapon/beam.h"
#include "ai/aigoals.h"
Expand Down Expand Up @@ -45,6 +46,7 @@ LabManager::LabManager() {
debris_init();
extern void debris_page_in();
debris_page_in();
props_level_init();
asteroid_level_init();
shockwave_level_init();
ship_level_init();
Expand Down Expand Up @@ -748,6 +750,12 @@ void LabManager::changeDisplayedObject(LabMode mode, int info_index, int subtype
ai_add_ship_goal_scripting(AI_GOAL_PLAY_DEAD_PERSISTENT, -1, 100, nullptr, &Ai_info[Player_ship->ai_index], 0, 0);
}
break;
case LabMode::Prop:
CurrentObject = prop_create(&CurrentOrientation, &CurrentPosition, CurrentClass);
if (isSafeForProps()) {
ModelFilename = Prop_info[CurrentClass].pof_file;
}
break;
case LabMode::Weapon:
if (ShowingTechModel && VALID_FNAME(Weapon_info[CurrentClass].tech_model)) {
ModelFilename = Weapon_info[CurrentClass].tech_model;
Expand Down
12 changes: 10 additions & 2 deletions code/lab/manager/lab_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "asteroid/asteroid.h"
#include "ship/ship.h"
#include "weapon/weapon.h"
#include "prop/prop.h"


enum class LabRotationMode { Both, Yaw, Pitch, Roll };
Expand Down Expand Up @@ -72,6 +73,9 @@ class LabManager {
// Unload any asteroids that were loaded
asteroid_level_close();

// Unload any props that were loaded
props_level_close();

// Lab can only be entered from the Mainhall so this should be safe
model_free_all();

Expand Down Expand Up @@ -103,11 +107,15 @@ class LabManager {

int Saved_cmdline_collisions_value;

bool isSafeForShips() {
bool isSafeForShips() const {
return CurrentMode == LabMode::Ship && CurrentObject != -1 && Objects[CurrentObject].type == OBJ_SHIP;
}

bool isSafeForWeapons() {
bool isSafeForProps() const {
return CurrentMode == LabMode::Prop && CurrentObject != -1 && Objects[CurrentObject].type == OBJ_PROP;
}

bool isSafeForWeapons() const {
bool valid = CurrentObject != -1 && (Objects[CurrentObject].type == OBJ_WEAPON || Objects[CurrentObject].type == OBJ_BEAM);
return CurrentMode == LabMode::Weapon && valid;
}
Expand Down
16 changes: 16 additions & 0 deletions code/lab/renderer/lab_renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "particle/particle.h"
#include "starfield/starfield.h"
#include "starfield/nebula.h"
#include "prop/prop.h"

#include "missionui/missionscreencommon.h"
#include "tracing/tracing.h"
Expand Down Expand Up @@ -114,6 +115,21 @@ void LabRenderer::renderModel(float frametime) {
}
}

if (obj->type == OBJ_PROP) {
prop* propp = prop_id_lookup(obj->instance);
propp->flags.set(Prop::Prop_Flags::Draw_as_wireframe, renderFlags[LabRenderFlag::ShowWireframe]);
propp->flags.set(Prop::Prop_Flags::Render_full_detail, renderFlags[LabRenderFlag::ShowFullDetail]);
propp->flags.set(Prop::Prop_Flags::Render_without_light,
renderFlags[LabRenderFlag::NoLighting] || currentMissionBackground == LAB_MISSION_NONE_STRING);
propp->flags.set(Prop::Prop_Flags::Render_without_diffuse, renderFlags[LabRenderFlag::NoDiffuseMap]);
propp->flags.set(Prop::Prop_Flags::Render_without_glowmap, renderFlags[LabRenderFlag::NoGlowMap]);
propp->flags.set(Prop::Prop_Flags::Render_without_normalmap, renderFlags[LabRenderFlag::NoNormalMap]);
propp->flags.set(Prop::Prop_Flags::Render_without_specmap, renderFlags[LabRenderFlag::NoSpecularMap]);
propp->flags.set(Prop::Prop_Flags::Render_without_reflectmap, renderFlags[LabRenderFlag::NoReflectMap]);
propp->flags.set(Prop::Prop_Flags::Render_without_heightmap, renderFlags[LabRenderFlag::NoHeightMap]);
propp->flags.set(Prop::Prop_Flags::Render_without_ambientmap, renderFlags[LabRenderFlag::NoAOMap]);
}

if (obj->type == OBJ_WEAPON) {
Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Draw_as_wireframe, renderFlags[LabRenderFlag::ShowWireframe]);
Weapons[obj->instance].weapon_flags.set(Weapon::Weapon_Flags::Render_full_detail, renderFlags[LabRenderFlag::ShowFullDetail]);
Expand Down
Loading