Skip to content

Commit cf5b50f

Browse files
authored
Qtfred visual tweaks (#7381)
* color variable or container rows based on type * add props and jump nodes to layer filters * make goals dialog dynamic and fix bugs * cleanup * missed these
1 parent bd6ae39 commit cf5b50f

17 files changed

Lines changed: 278 additions & 355 deletions

qtfred/src/mission/EditorViewport.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,18 @@ void EditorViewport::select_objects(const Marking_box& box) {
279279
}
280280

281281
break;
282+
283+
case OBJ_PROP:
284+
if (!view.Show_props) {
285+
valid = 0;
286+
}
287+
break;
288+
289+
case OBJ_JUMP_NODE:
290+
if (!view.Show_jump_nodes) {
291+
valid = 0;
292+
}
293+
break;
282294
}
283295

284296
g3_rotate_vertex(&v, &ptr->pos);
@@ -803,6 +815,14 @@ int EditorViewport::object_check_collision(object* objp, vec3d* p0, vec3d* p1, v
803815
}
804816
}
805817

818+
if ((objp->type == OBJ_PROP) && !view.Show_props) {
819+
return 0;
820+
}
821+
822+
if ((objp->type == OBJ_JUMP_NODE) && !view.Show_jump_nodes) {
823+
return 0;
824+
}
825+
806826
if (objp->flags[Object::Object_Flags::Hidden, Object::Object_Flags::Locked_from_editing]) {
807827
return 0;
808828
}

qtfred/src/mission/EditorViewport.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ struct ViewSettings {
3838
bool Lighting_on = false;
3939
bool FullDetail = false;
4040
bool Show_waypoints = true;
41+
bool Show_props = true;
42+
bool Show_jump_nodes = true;
4143
bool Show_compass = true;
4244
bool Highlight_selectable_subsys = false;
4345

qtfred/src/mission/FredRenderer.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,14 @@ void FredRenderer::display_ship_info(int cur_object_index) {
436436
}
437437
}
438438

439+
if ((objp->type == OBJ_PROP) && !view().Show_props) {
440+
render = 0;
441+
}
442+
443+
if ((objp->type == OBJ_JUMP_NODE) && !view().Show_jump_nodes) {
444+
render = 0;
445+
}
446+
439447
if (objp->flags[Object::Object_Flags::Hidden]) {
440448
render = 0;
441449
}
@@ -654,7 +662,7 @@ void FredRenderer::render_one_model_htl(object* objp,
654662
return;
655663

656664
if (objp->type == OBJ_JUMP_NODE) {
657-
return;
665+
return; // jump nodes have their own render loop in render_frame
658666
}
659667

660668
if ((objp->type == OBJ_WAYPOINT) && !view().Show_waypoints) {
@@ -675,6 +683,10 @@ void FredRenderer::render_one_model_htl(object* objp,
675683
}
676684
}
677685

686+
if ((objp->type == OBJ_PROP) && !view().Show_props) {
687+
return;
688+
}
689+
678690
if (objp->flags[Object::Object_Flags::Hidden]) {
679691
return;
680692
}
@@ -1031,10 +1043,12 @@ void FredRenderer::render_frame(int cur_object_index,
10311043
gr_set_color(0, 160, 0);
10321044

10331045
enable_htl();
1034-
for (auto& jn : Jump_nodes) {
1035-
const object* jnObj = jn.GetSCPObject();
1036-
if (jnObj != nullptr && _viewport->isObjectVisibleInLayer(jnObj)) {
1037-
jn.Render(&jnObj->pos);
1046+
if (view().Show_jump_nodes) {
1047+
for (auto& jn : Jump_nodes) {
1048+
const object* jnObj = jn.GetSCPObject();
1049+
if (jnObj != nullptr && _viewport->isObjectVisibleInLayer(jnObj)) {
1050+
jn.Render(&jnObj->pos);
1051+
}
10381052
}
10391053
}
10401054
disable_htl();

qtfred/src/mission/dialogs/LayerManagerDialogModel.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,11 @@ bool LayerManagerDialogModel::isDefaultLayer(const SCP_string& name) {
6565

6666
// --- Object type filters ---
6767

68-
bool LayerManagerDialogModel::getShowShips() const { return _viewport->view.Show_ships; }
69-
bool LayerManagerDialogModel::getShowStarts() const { return _viewport->view.Show_starts; }
70-
bool LayerManagerDialogModel::getShowWaypoints() const { return _viewport->view.Show_waypoints; }
68+
bool LayerManagerDialogModel::getShowShips() const { return _viewport->view.Show_ships; }
69+
bool LayerManagerDialogModel::getShowStarts() const { return _viewport->view.Show_starts; }
70+
bool LayerManagerDialogModel::getShowWaypoints() const { return _viewport->view.Show_waypoints; }
71+
bool LayerManagerDialogModel::getShowProps() const { return _viewport->view.Show_props; }
72+
bool LayerManagerDialogModel::getShowJumpNodes() const { return _viewport->view.Show_jump_nodes; }
7173

7274
void LayerManagerDialogModel::setShowShips(bool value) {
7375
if (_viewport->view.Show_ships != value) {
@@ -90,6 +92,20 @@ void LayerManagerDialogModel::setShowWaypoints(bool value) {
9092
}
9193
}
9294

95+
void LayerManagerDialogModel::setShowProps(bool value) {
96+
if (_viewport->view.Show_props != value) {
97+
_viewport->view.Show_props = value;
98+
_viewport->needsUpdate();
99+
}
100+
}
101+
102+
void LayerManagerDialogModel::setShowJumpNodes(bool value) {
103+
if (_viewport->view.Show_jump_nodes != value) {
104+
_viewport->view.Show_jump_nodes = value;
105+
_viewport->needsUpdate();
106+
}
107+
}
108+
93109
// --- IFF team filters ---
94110

95111
int LayerManagerDialogModel::getIffCount() {

qtfred/src/mission/dialogs/LayerManagerDialogModel.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ class LayerManagerDialogModel : public AbstractDialogModel {
2929
void setShowStarts(bool value);
3030
bool getShowWaypoints() const;
3131
void setShowWaypoints(bool value);
32+
bool getShowProps() const;
33+
void setShowProps(bool value);
34+
bool getShowJumpNodes() const;
35+
void setShowJumpNodes(bool value);
3236

3337
// IFF team filters
3438
static int getIffCount();

qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.cpp

Lines changed: 75 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "ShipGoalsDialogModel.h"
22
#include <globalincs/linklist.h>
33
#include <mission/object.h>
4+
#include <model/model.h>
45
namespace fso {
56
namespace fred {
67
namespace dialogs {
@@ -134,7 +135,6 @@ namespace fso {
134135

135136
void ShipGoalsDialogModel::update_item(const int item)
136137
{
137-
char* docker, * dockee, * subsys;
138138
ai_goal_mode mode;
139139
char save[80]{};
140140
SCP_string error_message;
@@ -179,30 +179,47 @@ namespace fso {
179179
case AI_GOAL_CHASE_SHIP_CLASS:
180180
break;
181181

182-
case AI_GOAL_DESTROY_SUBSYSTEM:
183-
subsys = nullptr;
184-
if (!m_multi_edit || (m_object[item] && (m_subsys[item].c_str() != nullptr)))
185-
subsys = (char*)m_subsys[item].c_str();
186-
//MODIFY(goalp[item].ai_submode, m_subsys[item] + 1);
187-
188-
if (!subsys) {
182+
case AI_GOAL_DESTROY_SUBSYSTEM: {
183+
if (m_subsys[item].empty()) {
189184
sprintf(error_message, "Order #%d doesn't have valid subsystem name. Order will be removed", item + 1);
190185
_viewport->dialogProvider->showButtonDialog(DialogType::Information,
191186
"Order Error",
192187
error_message,
193188
{ DialogButton::Ok });
194189
modify(goalp[item].ai_mode, AI_GOAL_NONE);
195190
return;
191+
}
196192

193+
// Look up the subsystem in the target ship to get a persistent name pointer.
194+
// Storing m_subsys[item].c_str() directly would dangle after the model is destroyed.
195+
const char* persistent_name = nullptr;
196+
int target_ship_idx = m_object[item] & DATA_MASK;
197+
if (target_ship_idx >= 0 && target_ship_idx < MAX_SHIPS) {
198+
ship_subsys* cur_ss = GET_FIRST(&Ships[target_ship_idx].subsys_list);
199+
while (cur_ss != END_OF_LIST(&Ships[target_ship_idx].subsys_list)) {
200+
if (!stricmp(cur_ss->system_info->subobj_name, m_subsys[item].c_str())) {
201+
persistent_name = cur_ss->system_info->subobj_name;
202+
break;
203+
}
204+
cur_ss = GET_NEXT(cur_ss);
205+
}
197206
}
198-
else {
199-
if (!goalp[item].docker.name || (goalp[item].docker.name && !stricmp(goalp[item].docker.name, subsys)))
200-
set_modified();
201207

202-
goalp[item].docker.name = subsys;
208+
if (!persistent_name) {
209+
sprintf(error_message, "Order #%d doesn't have valid subsystem name. Order will be removed", item + 1);
210+
_viewport->dialogProvider->showButtonDialog(DialogType::Information,
211+
"Order Error",
212+
error_message,
213+
{ DialogButton::Ok });
214+
modify(goalp[item].ai_mode, AI_GOAL_NONE);
215+
return;
203216
}
204217

218+
if (!goalp[item].docker.name || stricmp(goalp[item].docker.name, persistent_name) != 0)
219+
set_modified();
220+
goalp[item].docker.name = persistent_name;
205221
break;
222+
}
206223

207224
case AI_GOAL_CHASE:
208225
case AI_GOAL_CHASE_WING:
@@ -219,19 +236,37 @@ namespace fso {
219236

220237
break;
221238

222-
case AI_GOAL_DOCK:
223-
docker = nullptr;
224-
if (!m_multi_edit || (m_object[item] && (m_subsys[item].c_str() != nullptr)))
225-
docker = (char*)m_subsys[item].c_str();
226-
227-
dockee = nullptr;
228-
if (!m_multi_edit || (m_object[item] && (m_dock2[item] >= 0)))
229-
dockee = (char *)m_dock2[item];
239+
case AI_GOAL_DOCK: {
240+
// Resolve persistent dock bay name pointers from the polymodel.
241+
// Storing m_subsys[item].c_str() directly would dangle after the model is destroyed.
242+
char* docker = nullptr;
243+
char* dockee = nullptr;
244+
245+
if (!m_multi_edit || (m_object[item] && !m_subsys[item].empty())) {
246+
if (self_ship >= 0) {
247+
int model_num = Ship_info[Ships[self_ship].ship_info_index].model_num;
248+
polymodel* pm = model_get(model_num);
249+
if (pm) {
250+
for (int b = 0; b < pm->n_docks; b++) {
251+
if (!stricmp(pm->docking_bays[b].name, m_subsys[item].c_str())) {
252+
docker = pm->docking_bays[b].name;
253+
break;
254+
}
255+
}
256+
}
257+
}
258+
}
230259

231-
if (docker == (char*)SIZE_MAX)
232-
docker = nullptr;
233-
if (dockee == (char*)SIZE_MAX)
234-
dockee = nullptr;
260+
if (!m_multi_edit || (m_object[item] && (m_dock2[item] >= 0))) {
261+
int dockee_ship = m_object[item] & DATA_MASK;
262+
if (dockee_ship >= 0 && dockee_ship < MAX_SHIPS && m_dock2[item] >= 0) {
263+
int model_num = Ship_info[Ships[dockee_ship].ship_info_index].model_num;
264+
polymodel* pm = model_get(model_num);
265+
if (pm && m_dock2[item] < pm->n_docks) {
266+
dockee = pm->docking_bays[m_dock2[item]].name;
267+
}
268+
}
269+
}
235270

236271
if (!docker || !dockee) {
237272
sprintf(error_message, "Order #%d doesn't have valid docking points. Order will be removed", item + 1);
@@ -241,24 +276,18 @@ namespace fso {
241276
{ DialogButton::Ok });
242277
modify(goalp[item].ai_mode, AI_GOAL_NONE);
243278
return;
244-
245-
}
246-
else {
247-
if (!goalp[item].docker.name)
279+
} else {
280+
if (!goalp[item].docker.name || stricmp(goalp[item].docker.name, docker) != 0)
248281
set_modified();
249-
else if (!stricmp(goalp[item].docker.name, docker))
250-
set_modified();
251-
252-
if (!goalp[item].dockee.name)
253-
set_modified();
254-
else if (!stricmp(goalp[item].dockee.name, dockee))
282+
if (!goalp[item].dockee.name || stricmp(goalp[item].dockee.name, dockee) != 0)
255283
set_modified();
256284

257285
goalp[item].docker.name = docker;
258286
goalp[item].dockee.name = dockee;
259287
}
260288

261289
break;
290+
}
262291

263292
case AI_GOAL_GUARD:
264293
case AI_GOAL_GUARD_WING:
@@ -433,19 +462,19 @@ namespace fso {
433462
init_combo_data();
434463

435464
if (self_ship >= 0) {
436-
initialize(Ai_info[Ships[self_ship].ai_index].goals, self_ship);
465+
initialize(Ai_info[Ships[self_ship].ai_index].goals);
437466
}
438467
else if (self_wing >= 0) {
439-
initialize(Wings[self_wing].ai_goals, _editor->cur_ship);
468+
initialize(Wings[self_wing].ai_goals);
440469
}
441470
else {
442471
initialize_multi();
443472
}
444473
modelChanged();
445474
}
446-
void ShipGoalsDialogModel::initialize(ai_goal* goals, int ship)
475+
void ShipGoalsDialogModel::initialize(ai_goal* goals)
447476
{
448-
int i, item, num, inst, flag;
477+
int i, item, inst, flag;
449478
ai_goal_mode mode;
450479
object* ptr;
451480
SCP_vector<SCP_string> docks;
@@ -522,22 +551,16 @@ namespace fso {
522551
break;
523552

524553
case AI_GOAL_DESTROY_SUBSYSTEM:
525-
num = ship_name_lookup(goalp[item].target_name, 1);
526-
if (num != -1)
527-
m_subsys[item] = ship_find_subsys(&Ships[num], goalp[item].docker.name);
528-
554+
// docker.name already holds the subsystem name string... copy it directly.
555+
// (ship_find_subsys returns an int index, not the name, so don't use it here.)
556+
if (goalp[item].docker.name != nullptr)
557+
m_subsys[item] = goalp[item].docker.name;
529558
break;
530559

531560
case AI_GOAL_DOCK:
532-
m_subsys[item] = -1;
533-
docks = _editor->get_docking_list(Ship_info[Ships[ship].ship_info_index].model_num);
534-
for (i = 0; unsigned(i) < docks.size(); i++) {
535-
if (!stricmp(goalp[item].docker.name, docks[i].c_str())) {
536-
m_subsys[item] = i;
537-
break;
538-
}
539-
}
540-
561+
// Store the docker bay name string directly; the persistent pointer lives in the polymodel.
562+
if (goalp[item].docker.name != nullptr)
563+
m_subsys[item] = goalp[item].docker.name;
541564
break;
542565

543566
case AI_GOAL_CHASE_WING:
@@ -647,7 +670,7 @@ namespace fso {
647670
ptr = GET_FIRST(&obj_used_list);
648671
while (ptr != END_OF_LIST(&obj_used_list)) {
649672
if (((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START)) && (ptr->flags[Object::Object_Flags::Marked])) {
650-
initialize(Ai_info[Ships[ptr->instance].ai_index].goals, ptr->instance);
673+
initialize(Ai_info[Ships[ptr->instance].ai_index].goals);
651674
if (!flag) {
652675
flag = 1;
653676
for (i = 0; i < ED_MAX_GOALS; i++) {

qtfred/src/mission/dialogs/ShipEditor/ShipGoalsDialogModel.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33
#include "../AbstractDialogModel.h"
44

5+
#include "ai/ai.h"
56
#include "ai/aigoals.h"
67

78
namespace fso {
89
namespace fred {
910
namespace dialogs {
10-
constexpr auto ED_MAX_GOALS = 10;
11+
constexpr auto ED_MAX_GOALS = MAX_AI_GOALS;
1112
constexpr auto MAX_EDITOR_GOAL_PRIORITY = 200;
1213
constexpr auto TYPE_PATH = 0x1000;
1314
constexpr auto TYPE_SHIP = 0x2000;
@@ -22,7 +23,7 @@ constexpr auto MAX_VALID = 99;
2223
class ShipGoalsDialogModel : public AbstractDialogModel {
2324
private:
2425
int Ai_goal_list_size = Editor::getAigoal_list_size();
25-
void initialize(ai_goal* goals, int ship);
26+
void initialize(ai_goal* goals);
2627
void initialize_multi();
2728
void init_combo_data();
2829

0 commit comments

Comments
 (0)