Files
fs2open.github.com/fred2/briefingeditordlg.cpp
Goober5000andClaude Opus 5 09fc7055b5 Fix FRED and qtFRED memory leaks at exit (#7784)
Neither editor had a teardown to match FSO's game_shutdown(), so the engine
data they set up was never freed.  Add editor_free_engine_data() to free it,
and call it from a new fred_shutdown() in FRED and from qtFRED's shutdown(),
before the graphics system closes.

Also:
- Delete FRED's modeless editors if they are still open at exit.
- Make FRED's dialog tooltips and sexp tree image lists members, since
  they were leaked or depended on OnDestroy() being called.
- Parent qtFRED's item models so they are deleted with their widgets.
- Enable the CRT leak report in qtFRED, which previously had none.
- In qtFRED, call shutdown() last in closeEvent(), since it destroys the
  window.
- Make the preferences path and user SID strings function-level statics,
  so MFC's leak dump no longer reports them as leaks.

Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
2026-09-21 05:12:35 +02:00

1536 lines
42 KiB
C++

/*
* Copyright (C) Volition, Inc. 1999. All rights reserved.
*
* All source code herein is the property of Volition, Inc. You may not sell
* or otherwise commercially exploit the source or things you created based on the
* source.
*
*/
#include "stdafx.h"
#include "FRED.h"
#include "BriefingEditorDlg.h"
#include "FREDDoc.h"
#include "mission/missionbriefcommon.h"
#include "mission/missionparse.h"
#include "FredRender.h"
#include "Management.h"
#include "globalincs/alphacolors.h"
#include "globalincs/linklist.h"
#include "MainFrm.h"
#include "bmpman/bmpman.h"
#include "gamesnd/eventmusic.h"
#include "starfield/starfield.h"
#include "jumpnode/jumpnode.h"
#include "cfile/cfile.h"
#include "object/objectdock.h"
#include "iff_defs/iff_defs.h"
#include "sound/audiostr.h"
#include "localization/localize.h"
#include "mod_table/mod_table.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define ID_MENU 9000
static int Max_icons_for_lines;
/////////////////////////////////////////////////////////////////////////////
// briefing_editor_dlg dialog
briefing_editor_dlg::briefing_editor_dlg(CWnd* pParent /*=NULL*/)
: CDialog(briefing_editor_dlg::IDD, pParent)
{
int i, z;
// figure out max icons we can have with lines to each other less than max allowed lines.
// Basically, # lines = (# icons - 1) factorial
Max_icons_for_lines = 0;
do {
i = ++Max_icons_for_lines + 1;
z = 0;
while (--i)
z += i;
} while (z < MAX_BRIEF_STAGE_LINES);
//{{AFX_DATA_INIT(briefing_editor_dlg)
m_hilight = FALSE;
m_icon_image = -1;
m_icon_label = _T("");
m_icon_closeup_label = _T("");
m_icon_scale = -1;
m_stage_title = _T("");
m_text = _T("");
m_time = _T("");
m_voice = _T("");
m_icon_team = -1;
m_ship_type = -1;
m_change_local = FALSE;
m_id = 0;
m_briefing_music = -1;
m_substitute_briefing_music = _T("");
m_cut_next = FALSE;
m_cut_prev = FALSE;
m_no_grid = FALSE;
m_current_briefing = -1;
m_flipicon = FALSE;
m_use_wing = FALSE;
m_use_cargo = FALSE;
//}}AFX_DATA_INIT
m_play_icon = nullptr;
m_voice_id = -1;
m_cur_stage = 0;
m_last_stage = m_cur_icon = m_last_icon = -1;
m_tree._model.modified = &modified; // provide way to indicate trees are modified in dialog
// copy view initialization
m_copy_view_set = 0;
}
void briefing_editor_dlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(briefing_editor_dlg)
DDX_Control(pDX, IDC_TREE, m_tree);
DDX_Control(pDX, IDC_LINES, m_lines);
DDX_Check(pDX, IDC_HILIGHT, m_hilight);
DDX_CBIndex(pDX, IDC_ICON_IMAGE, m_icon_image);
DDX_Text(pDX, IDC_ICON_LABEL, m_icon_label);
DDV_MaxChars(pDX, m_icon_label, MAX_LABEL_LEN - 1);
DDX_Text(pDX, IDC_ICON_CLOSEUP_LABEL, m_icon_closeup_label);
DDV_MaxChars(pDX, m_icon_closeup_label, MAX_LABEL_LEN - 1);
DDX_Text(pDX, IDC_ICON_SCALE, m_icon_scale);
DDX_Text(pDX, IDC_STAGE_TITLE, m_stage_title);
DDX_Text(pDX, IDC_TEXT, m_text);
DDX_Text(pDX, IDC_TIME, m_time);
DDX_Text(pDX, IDC_VOICE, m_voice);
DDV_MaxChars(pDX, m_voice, MAX_FILENAME_LEN - 1);
DDX_CBIndex(pDX, IDC_TEAM, m_icon_team);
DDX_CBIndex(pDX, IDC_SHIP_TYPE, m_ship_type);
DDX_Check(pDX, IDC_LOCAL, m_change_local);
DDX_Text(pDX, IDC_ID, m_id);
DDX_CBIndex(pDX, IDC_BRIEFING_MUSIC, m_briefing_music);
DDX_Text(pDX, IDC_SUBSTITUTE_BRIEFING_MUSIC, m_substitute_briefing_music);
DDX_Check(pDX, IDC_CUT_NEXT, m_cut_next);
DDX_Check(pDX, IDC_CUT_PREV, m_cut_prev);
DDX_Check(pDX, IDC_NO_GRID, m_no_grid);
DDX_Check(pDX, IDC_FLIP_ICON, m_flipicon);
DDX_Check(pDX, IDC_USE_WING_ICON, m_use_wing);
DDX_Check(pDX, IDC_USE_CARGO_ICON, m_use_cargo);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(briefing_editor_dlg, CDialog)
//{{AFX_MSG_MAP(briefing_editor_dlg)
ON_WM_CLOSE()
ON_BN_CLICKED(IDC_NEXT, OnNext)
ON_BN_CLICKED(IDC_PREV, OnPrev)
ON_BN_CLICKED(IDC_BROWSE, OnBrowse)
ON_BN_CLICKED(IDC_ADD_STAGE, OnAddStage)
ON_BN_CLICKED(IDC_DELETE_STAGE, OnDeleteStage)
ON_BN_CLICKED(IDC_INSERT_STAGE, OnInsertStage)
ON_BN_CLICKED(IDC_MAKE_ICON, OnMakeIcon)
ON_BN_CLICKED(IDC_DELETE_ICON, OnDeleteIcon)
ON_BN_CLICKED(IDC_GOTO_VIEW, OnGotoView)
ON_BN_CLICKED(IDC_SAVE_VIEW, OnSaveView)
ON_CBN_SELCHANGE(IDC_ICON_IMAGE, OnSelchangeIconImage)
ON_CBN_SELCHANGE(IDC_TEAM, OnSelchangeTeam)
ON_CBN_SELCHANGE(IDC_SHIP_TYPE, OnSelchangeShipType)
ON_BN_CLICKED(IDC_PROPAGATE_ICONS, OnPropagateIcons)
ON_WM_INITMENU()
ON_BN_CLICKED(IDC_LINES, OnLines)
ON_NOTIFY(NM_RCLICK, IDC_TREE, OnRclickTree)
ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_TREE, OnBeginlabeleditTree)
ON_NOTIFY(TVN_ENDLABELEDIT, IDC_TREE, OnEndlabeleditTree)
ON_BN_CLICKED(IDC_PLAY, OnPlay)
ON_BN_CLICKED(IDC_COPY_VIEW, OnCopyView)
ON_BN_CLICKED(IDC_PASTE_VIEW, OnPasteView)
ON_BN_CLICKED(IDC_FLIP_ICON, OnFlipIcon)
ON_BN_CLICKED(IDC_USE_WING_ICON, OnWingIcon)
ON_BN_CLICKED(IDC_USE_CARGO_ICON, OnCargoIcon)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// briefing_editor_dlg message handlers
BOOL briefing_editor_dlg::OnInitDialog()
{
CDialog::OnInitDialog();
//create tool tip controls
m_CloseupLabelToolTip.Create(this);
CWnd* pWnd = GetDlgItem(IDC_ICON_CLOSEUP_LABEL);
m_CloseupLabelToolTip.AddTool(pWnd, "If this is blank, the ship class will be used");
m_CloseupLabelToolTip.Activate(TRUE);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
BOOL briefing_editor_dlg::PreTranslateMessage(MSG* pMsg)
{
m_CloseupLabelToolTip.RelayEvent(pMsg);
return CDialog::PreTranslateMessage(pMsg);
}
void briefing_editor_dlg::OnInitMenu(CMenu* pMenu)
{
int i;
CMenu *m;
// disable any items we should disable
m = pMenu->GetSubMenu(0);
// uncheck all menu items
for (i=0; i<Num_teams; i++)
m->CheckMenuItem(i, MF_BYPOSITION | MF_UNCHECKED);
for (i=Num_teams; i<MAX_TVT_TEAMS; i++)
m->EnableMenuItem(i, MF_BYPOSITION | MF_GRAYED);
// put a check next to the currently selected item
m->CheckMenuItem(m_current_briefing, MF_BYPOSITION | MF_CHECKED );
// Karajorma - it might be nice to autobalance the briefings and debriefings but I'm not doing anything till I
// understand how the how system works better. disabling the option for now.
m = pMenu->GetSubMenu(1);
m->EnableMenuItem(ID_AUTOBALANCE, MF_GRAYED);
CDialog::OnInitMenu(pMenu);
}
void briefing_editor_dlg::create()
{
int i;
CComboBox *box;
CDialog::Create(IDD);
theApp.init_window(&Briefing_wnd_data, this);
box = (CComboBox *) GetDlgItem(IDC_ICON_IMAGE);
for (i=0; i<MIN_BRIEF_ICONS; i++)
box->AddString(Icon_names[i]);
box = (CComboBox *) GetDlgItem(IDC_TEAM);
for (i=0; i< (int)Iff_info.size(); i++)
box->AddString(Iff_info[i].iff_name);
box = (CComboBox *) GetDlgItem(IDC_SHIP_TYPE);
for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it)
box->AddString(it->name);
box = (CComboBox *) GetDlgItem(IDC_BRIEFING_MUSIC);
box->AddString("None");
for (auto &sm: Spooled_music)
box->AddString(sm.name);
box = (CComboBox *) GetDlgItem(IDC_SUBSTITUTE_BRIEFING_MUSIC);
box->AddString("None");
for (auto &sm: Spooled_music)
box->AddString(sm.name);
m_play_icon = load_button_icon(IDB_PLAY, RGB(192, 192, 192));
((CButton *) GetDlgItem(IDC_PLAY)) -> SetIcon(m_play_icon);
m_current_briefing = 0;
Briefing = &Briefings[m_current_briefing];
m_briefing_music = Mission_music[SCORE_BRIEFING] + 1;
m_substitute_briefing_music = The_mission.substitute_briefing_music_name;
UpdateData(FALSE);
update_data();
OnGotoView();
update_map_window();
}
void briefing_editor_dlg::focus_sexp(int select_sexp_node)
{
int i, t, n;
n = m_tree.select_sexp_node = select_sexp_node;
if (n == -1)
return;
for (t = 0; t < Num_teams; t++) {
for (i = 0; i < Briefings[t].num_stages; i++) {
if (query_node_in_sexp(n, Briefings[t].stages[i].formula)) {
m_current_briefing = t;
Briefing = &Briefings[t];
m_cur_stage = i;
update_data();
GetDlgItem(IDC_TREE)->SetFocus();
m_tree.hilite_item(m_tree.select_sexp_node);
return;
}
}
}
}
void briefing_editor_dlg::OnOK()
{
}
void briefing_editor_dlg::OnCancel()
{
OnClose();
}
void briefing_editor_dlg::OnClose()
{
int bs, i, j, s, t, dup = 0;
briefing_editor_dlg *ptr;
brief_stage *sp;
m_cur_stage = -1;
update_data(1);
audiostream_close_file(m_voice_id, 0);
for ( bs = 0; bs < Num_teams; bs++ ) {
for (s=0; s<Briefing[bs].num_stages; s++) {
sp = &Briefing[bs].stages[s];
t = sp->num_icons;
for (i=0; i<t-1; i++)
for (j=i+1; j<t; j++) {
if ((sp->icons[i].id >= 0) && (sp->icons[i].id == sp->icons[j].id))
dup = 1;
}
}
}
if (dup)
MessageBox("You have duplicate icon names. You should resolve these.", "Warning");
theApp.record_window_data(&Briefing_wnd_data, this);
ptr = Briefing_dialog; // this juggling prevents a crash in certain situations
Briefing_dialog = nullptr;
delete ptr;
FREDDoc_ptr->autosave("briefing editor");
}
void briefing_editor_dlg::reset_editor()
{
audiostream_close_file(m_voice_id, 0);
m_voice_id = -1;
m_cur_stage = -1;
update_data(0);
}
// some kind of hackish code to get around the problem if saving (which implicitly loads,
// which implicitly clears all mission info) not affecting the editor's state at save.
void briefing_editor_dlg::save_editor_state()
{
stage_saved = m_cur_stage;
icon_saved = m_cur_icon;
}
void briefing_editor_dlg::restore_editor_state()
{
m_cur_stage = stage_saved;
m_cur_icon = icon_saved;
}
void briefing_editor_dlg::update_data(int update)
{
char buf[MAX_LABEL_LEN];
SCP_string buf3;
int i, j, l, lines, count, enable = TRUE, valid = 0, invalid = 0;
object *objp;
brief_stage *ptr = NULL;
if (update)
UpdateData(TRUE);
// save off current data before we update over it with new briefing stage/team stuff
Briefing = save_briefing;
Mission_music[SCORE_BRIEFING] = m_briefing_music - 1;
strcpy_s(The_mission.substitute_briefing_music_name, m_substitute_briefing_music);
if (m_last_stage >= 0) {
ptr = &Briefing->stages[m_last_stage];
deconvert_multiline_string(buf3, m_text);
lcl_fred_replace_stuff(buf3);
if (ptr->text != buf3)
set_modified();
ptr->text = buf3;
MODIFY(ptr->camera_time, atoi(m_time));
string_copy(ptr->voice, m_voice, MAX_FILENAME_LEN - 1, 1);
i = ptr->flags;
if (m_cut_prev)
i |= BS_BACKWARD_CUT;
else
i &= ~BS_BACKWARD_CUT;
if (m_cut_next)
i |= BS_FORWARD_CUT;
else
i &= ~BS_FORWARD_CUT;
if (m_no_grid)
ptr->draw_grid = false;
else
ptr->draw_grid = true;
MODIFY(ptr->flags, i);
ptr->formula = m_tree._model.save_tree();
switch (m_lines.GetCheck()) {
case 1:
// add lines between every pair of 2 marked icons if there isn't one already.
for (i=0; i<ptr->num_icons - 1; i++)
for (j=i+1; j<ptr->num_icons; j++) {
if ( icon_marked[i] && icon_marked[j] ) {
for (l=0; l<ptr->num_lines; l++)
if ( ((ptr->lines[l].start_icon == i) && (ptr->lines[l].end_icon == j)) || ((ptr->lines[l].start_icon == j) && (ptr->lines[l].end_icon == i)) )
break;
if ((l == ptr->num_lines) && (l < MAX_BRIEF_STAGE_LINES)) {
ptr->lines[l].start_icon = i;
ptr->lines[l].end_icon = j;
ptr->num_lines++;
}
}
}
break;
case 0:
// remove all existing lines between any 2 marked icons
i = ptr->num_lines;
while (i--)
if ( icon_marked[ptr->lines[i].start_icon] && icon_marked[ptr->lines[i].end_icon] ) {
ptr->num_lines--;
for (l=i; l<ptr->num_lines; l++)
ptr->lines[l] = ptr->lines[l + 1];
}
break;
}
//WMC - Safeguard against broken lines
i = ptr->num_lines;
while(i--)
{
if(ptr->lines[i].start_icon < 0 || ptr->lines[i].end_icon < 0 || ptr->lines[i].start_icon >= ptr->num_icons || ptr->lines[i].end_icon >= ptr->num_icons)
{
ptr->num_lines--;
for(l = i; l < ptr->num_lines; l++)
ptr->lines[l] = ptr->lines[l+1];
}
}
if (m_last_icon >= 0) {
valid = (m_id != ptr->icons[m_last_icon].id);
if (m_id < 0) {
// briefing icon ids must never be negative; revert to the previous id
if (valid) {
char msg[1024];
sprintf(msg, "Icon ID #%d is invalid. Briefing icon IDs cannot be negative.\n"
"Icon ID has been reset back to %d", m_id, ptr->icons[m_last_icon].id);
m_id = ptr->icons[m_last_icon].id;
MessageBox(msg);
}
} else {
if (valid && !m_change_local) {
for (i=m_last_stage+1; i<Briefing->num_stages; i++) {
if (find_icon(m_id, i) >= 0) {
char msg[1024];
valid = 0;
sprintf(msg, "Icon ID #%d is already used in a later stage. You can only\n"
"change to that ID locally. Icon ID has been reset back to %d", m_id, ptr->icons[m_last_icon].id);
m_id = ptr->icons[m_last_icon].id;
MessageBox(msg);
break;
}
}
}
for (i=0; i<ptr->num_icons; i++)
if ((i != m_last_icon) && (ptr->icons[i].id == m_id)) {
char msg[1024];
sprintf(msg, "Icon ID #%d is already used in this stage. Icon ID has been reset back to %d",
m_id, ptr->icons[m_last_icon].id);
m_id = ptr->icons[m_last_icon].id;
MessageBox(msg);
break;
}
if (valid && !m_change_local) {
set_modified();
reset_icon_loop(m_last_stage);
while (get_next_icon(ptr->icons[m_last_icon].id))
iconp->id = m_id;
}
}
ptr->icons[m_last_icon].id = m_id;
string_copy(buf, m_icon_label, MAX_LABEL_LEN - 1);
lcl_fred_replace_stuff(buf, MAX_LABEL_LEN - 1);
if (strcmp(ptr->icons[m_last_icon].label, buf) && !m_change_local) {
set_modified();
reset_icon_loop(m_last_stage);
while (get_next_icon(m_id))
strcpy_s(iconp->label, buf);
}
strcpy_s(ptr->icons[m_last_icon].label, buf);
string_copy(buf, m_icon_closeup_label, MAX_LABEL_LEN - 1);
lcl_fred_replace_stuff(buf, MAX_LABEL_LEN - 1);
if (strcmp(ptr->icons[m_last_icon].closeup_label, buf) && !m_change_local) {
set_modified();
reset_icon_loop(m_last_stage);
while (get_next_icon(m_id))
strcpy_s(iconp->closeup_label, buf);
}
strcpy_s(ptr->icons[m_last_icon].closeup_label, buf);
if (m_icon_scale > 0) {
float new_scale = m_icon_scale / 100.0f;
if ((ptr->icons[m_last_icon].scale_factor != new_scale) && !m_change_local) {
set_modified();
reset_icon_loop(m_last_stage);
while (get_next_icon(m_id))
iconp->scale_factor = new_scale;
}
ptr->icons[m_last_icon].scale_factor = new_scale;
}
// build the new flags for this icon from the checkboxes, preserving any non-editable bits
const int editable_flags = BI_HIGHLIGHT | BI_MIRROR_ICON | BI_USE_WING_ICON | BI_USE_CARGO_ICON;
int new_flags = ptr->icons[m_last_icon].flags;
if (m_hilight) new_flags |= BI_HIGHLIGHT; else new_flags &= ~BI_HIGHLIGHT;
if (m_flipicon) new_flags |= BI_MIRROR_ICON; else new_flags &= ~BI_MIRROR_ICON;
if (m_use_wing) new_flags |= BI_USE_WING_ICON; else new_flags &= ~BI_USE_WING_ICON;
if (m_use_cargo) new_flags |= BI_USE_CARGO_ICON; else new_flags &= ~BI_USE_CARGO_ICON;
if ((ptr->icons[m_last_icon].flags != new_flags) && !m_change_local) {
set_modified();
reset_icon_loop(m_last_stage);
// apply only the editable bits to later icons, preserving their other flags
while (get_next_icon(m_id))
iconp->flags = (iconp->flags & ~editable_flags) | (new_flags & editable_flags);
}
ptr->icons[m_last_icon].flags = new_flags;
if ((ptr->icons[m_last_icon].type != m_icon_image) && !m_change_local) {
set_modified();
reset_icon_loop(m_last_stage);
while (get_next_icon(m_id))
iconp->type = m_icon_image;
}
ptr->icons[m_last_icon].type = m_icon_image;
if ((ptr->icons[m_last_icon].team != m_icon_team) && !m_change_local) {
set_modified();
reset_icon_loop(m_last_stage);
while (get_next_icon(m_id))
iconp->team = m_icon_team;
}
ptr->icons[m_last_icon].team = m_icon_team;
if ((ptr->icons[m_last_icon].ship_class != m_ship_type) && !m_change_local) {
set_modified();
reset_icon_loop(m_last_stage);
while (get_next_icon(m_id))
iconp->ship_class = m_ship_type;
}
MODIFY(ptr->icons[m_last_icon].ship_class, m_ship_type);
}
}
if (!::IsWindow(m_hWnd))
return;
// set briefing pointer to correct team
Briefing = &Briefings[m_current_briefing];
if ((m_cur_stage >= 0) && (m_cur_stage < Briefing->num_stages)) {
ptr = &Briefing->stages[m_cur_stage];
m_stage_title.Format("Stage %d of %d", m_cur_stage + 1, Briefing->num_stages);
convert_multiline_string(m_text, ptr->text);
m_time.Format("%d", ptr->camera_time);
m_voice = ptr->voice;
m_cut_prev = (ptr->flags & BS_BACKWARD_CUT) ? 1 : 0;
m_cut_next = (ptr->flags & BS_FORWARD_CUT) ? 1 : 0;
m_no_grid = (ptr->draw_grid) ? 0 : 1;
m_tree.load_tree(ptr->formula);
} else {
m_stage_title = _T("No stages");
m_text = _T("");
m_time = _T("");
m_voice = _T("");
m_cut_prev = m_cut_next = 0;
m_no_grid = 0;
m_tree.clear_tree();
enable = FALSE;
m_cur_stage = -1;
}
if (m_cur_stage == Briefing->num_stages - 1)
GetDlgItem(IDC_NEXT) -> EnableWindow(FALSE);
else
GetDlgItem(IDC_NEXT) -> EnableWindow(enable);
if (m_cur_stage)
GetDlgItem(IDC_PREV) -> EnableWindow(enable);
else
GetDlgItem(IDC_PREV) -> EnableWindow(FALSE);
if (Briefing->num_stages >= MAX_BRIEF_STAGES)
GetDlgItem(IDC_ADD_STAGE) -> EnableWindow(FALSE);
else
GetDlgItem(IDC_ADD_STAGE) -> EnableWindow(TRUE);
if (Briefing->num_stages) {
GetDlgItem(IDC_DELETE_STAGE) -> EnableWindow(enable);
GetDlgItem(IDC_INSERT_STAGE) -> EnableWindow(enable);
} else {
GetDlgItem(IDC_DELETE_STAGE) -> EnableWindow(FALSE);
GetDlgItem(IDC_INSERT_STAGE) -> EnableWindow(FALSE);
}
GetDlgItem(IDC_TIME) -> EnableWindow(enable);
GetDlgItem(IDC_VOICE) -> EnableWindow(enable);
GetDlgItem(IDC_BROWSE) -> EnableWindow(enable);
GetDlgItem(IDC_TEXT) -> EnableWindow(enable);
GetDlgItem(IDC_SAVE_VIEW) -> EnableWindow(enable);
GetDlgItem(IDC_GOTO_VIEW) -> EnableWindow(enable);
GetDlgItem(IDC_CUT_PREV) -> EnableWindow(enable);
GetDlgItem(IDC_CUT_NEXT) -> EnableWindow(enable);
GetDlgItem(IDC_NO_GRID)->EnableWindow(enable);
GetDlgItem(IDC_TREE) -> EnableWindow(enable);
GetDlgItem(IDC_PLAY) -> EnableWindow(enable);
if ((m_cur_stage >= 0) && (m_cur_icon >= 0) && (m_cur_icon < ptr->num_icons)) {
m_hilight = (ptr->icons[m_cur_icon].flags & BI_HIGHLIGHT)?1:0;
m_flipicon = (ptr->icons[m_cur_icon].flags & BI_MIRROR_ICON)?1:0;
m_use_wing = (ptr->icons[m_cur_icon].flags & BI_USE_WING_ICON)?1:0;
m_use_cargo = (ptr->icons[m_cur_icon].flags & BI_USE_CARGO_ICON)?1:0;
m_icon_image = ptr->icons[m_cur_icon].type;
m_icon_team = ptr->icons[m_cur_icon].team;
m_icon_label = ptr->icons[m_cur_icon].label;
m_icon_closeup_label = ptr->icons[m_cur_icon].closeup_label;
m_icon_scale = static_cast<int>(ptr->icons[m_cur_icon].scale_factor * 100.0f);
m_ship_type = ptr->icons[m_cur_icon].ship_class;
m_id = ptr->icons[m_cur_icon].id;
enable = TRUE;
} else {
m_flipicon = FALSE;
m_hilight = FALSE;
m_use_wing = FALSE;
m_use_cargo = FALSE;
m_icon_image = -1;
m_icon_team = -1;
m_ship_type = -1;
m_icon_label = _T("");
m_icon_closeup_label = _T("");
m_icon_scale = 100;
m_cur_icon = -1;
m_id = 0;
enable = FALSE;
}
// see if icon is overridden by ships.tbl
int sip_bii_ship = (m_ship_type >= 0) ? Ship_info[m_ship_type].bii_index_ship : -1;
int sip_bii_wing = (sip_bii_ship >= 0) ? Ship_info[m_ship_type].bii_index_wing : -1;
int sip_bii_cargo = (sip_bii_ship >= 0) ? Ship_info[m_ship_type].bii_index_ship_with_cargo : -1;
GetDlgItem(IDC_USE_WING_ICON) -> ShowWindow(sip_bii_wing >= 0);
GetDlgItem(IDC_USE_CARGO_ICON) -> ShowWindow(sip_bii_cargo >= 0);
GetDlgItem(IDC_ICON_CLOSEUP_LABEL) -> EnableWindow(enable);
GetDlgItem(IDC_ICON_LABEL) -> EnableWindow(enable);
GetDlgItem(IDC_ICON_IMAGE) -> EnableWindow(enable);
GetDlgItem(IDC_SHIP_TYPE) -> EnableWindow(enable);
GetDlgItem(IDC_HILIGHT) -> EnableWindow(enable);
GetDlgItem(IDC_FLIP_ICON) -> EnableWindow(enable);
GetDlgItem(IDC_USE_WING_ICON) -> EnableWindow(enable);
GetDlgItem(IDC_USE_CARGO_ICON) -> EnableWindow(enable);
GetDlgItem(IDC_LOCAL) -> EnableWindow(enable);
GetDlgItem(IDC_TEAM) -> EnableWindow(enable);
GetDlgItem(IDC_ID) -> EnableWindow(enable);
GetDlgItem(IDC_DELETE_ICON) -> EnableWindow(enable);
valid = invalid = 0;
objp = GET_FIRST(&obj_used_list);
while (objp != END_OF_LIST(&obj_used_list)) {
if (objp->flags[Object::Object_Flags::Marked]) {
if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START) || (objp->type == OBJ_WAYPOINT) || (objp->type == OBJ_JUMP_NODE))
valid = 1;
else
invalid = 1;
}
objp = GET_NEXT(objp);
}
if (m_cur_stage >= 0)
ptr = &Briefing->stages[m_cur_stage];
if (valid && !invalid && (m_cur_stage >= 0) && (ptr->num_icons < MAX_STAGE_ICONS))
GetDlgItem(IDC_MAKE_ICON) -> EnableWindow(TRUE);
else
GetDlgItem(IDC_MAKE_ICON) -> EnableWindow(FALSE);
if (m_cur_stage >= 0)
for (i=0; i<ptr->num_icons; i++)
icon_marked[i] = 0;
valid = invalid = 0;
objp = GET_FIRST(&obj_used_list);
while (objp != END_OF_LIST(&obj_used_list)) {
if (objp->flags[Object::Object_Flags::Marked]) {
if (objp->type == OBJ_POINT) {
valid++;
icon_marked[objp->instance] = 1;
} else
invalid++;
}
objp = GET_NEXT(objp);
}
if (valid && !invalid && (m_cur_stage >= 0))
GetDlgItem(IDC_PROPAGATE_ICONS) -> EnableWindow(TRUE);
else
GetDlgItem(IDC_PROPAGATE_ICONS) -> EnableWindow(FALSE);
count = 0;
lines = 1; // default lines checkbox to checked
if (m_cur_stage >= 0) {
for (i=0; i<ptr->num_lines; i++)
line_marked[i] = 0;
// go through and locate all lines between marked icons
for (i=0; i<ptr->num_icons - 1; i++)
for (j=i+1; j<ptr->num_icons; j++) {
if ( icon_marked[i] && icon_marked[j] ) {
for (l=0; l<ptr->num_lines; l++)
if ( ((ptr->lines[l].start_icon == i) && (ptr->lines[l].end_icon == j)) || ((ptr->lines[l].start_icon == j) && (ptr->lines[l].end_icon == i)) ) {
line_marked[l] = 1;
count++; // track number of marked lines (lines between 2 icons that are both marked)
break;
}
// at least 1 line missing between 2 marked icons, so use mixed state
if (l == ptr->num_lines)
lines = 2;
}
}
}
// not even 1 line between any 2 marked icons? Set checkbox to unchecked.
if (!count)
lines = 0;
i = 0;
if (m_cur_stage >= 0){
i = calc_num_lines_for_icons(valid) + ptr->num_lines - count;
}
if ((valid > 1) && !invalid && (m_cur_stage >= 0) && (i <= MAX_BRIEF_STAGE_LINES))
GetDlgItem(IDC_LINES) -> EnableWindow(TRUE);
else
GetDlgItem(IDC_LINES) -> EnableWindow(FALSE);
m_lines.SetCheck(lines);
UpdateData(FALSE);
if ((m_last_stage != m_cur_stage) || (Briefing != save_briefing)) {
if (m_last_stage >= 0) {
for (i=0; i<save_briefing->stages[m_last_stage].num_icons; i++) {
// save positions of all icons, in case they have moved
save_briefing->stages[m_last_stage].icons[i].pos = Objects[icon_obj[i]].pos;
// release objects being used by last stage
obj_delete(icon_obj[i]);
}
}
if (m_cur_stage >= 0) {
flagset<Object::Object_Flags> default_flags;
for (i=0; i<ptr->num_icons; i++) {
// create an object for each icon for display/manipulation purposes
icon_obj[i] = obj_create(OBJ_POINT, -1, i, NULL, &ptr->icons[i].pos, 0.0f, default_flags + Object::Object_Flags::Renders);
}
obj_merge_created_list();
}
m_last_stage = m_cur_stage;
}
m_last_icon = m_cur_icon;
Update_window = 1;
save_briefing = Briefing;
}
// Given a number of icons, figure out how many lines would be required to connect each one
// with all of the others.
//
int briefing_editor_dlg::calc_num_lines_for_icons(int num)
{
int lines = 0;
if (num < 2)
return 0;
// Basically, this is # lines = (# icons - 1) factorial
while (--num)
lines += num;
return lines;
}
void briefing_editor_dlg::OnNext()
{
m_cur_stage++;
m_cur_icon = -1;
audiostream_close_file(m_voice_id, 0);
m_voice_id = -1;
update_data();
OnGotoView();
}
void briefing_editor_dlg::OnPrev()
{
m_cur_stage--;
m_cur_icon = -1;
audiostream_close_file(m_voice_id, 0);
m_voice_id = -1;
update_data();
OnGotoView();
}
void briefing_editor_dlg::OnBrowse()
{
int z;
CString name;
UpdateData(TRUE);
audiostream_close_file(m_voice_id, 0);
m_voice_id = -1;
if (The_mission.game_type & MISSION_TYPE_TRAINING)
z = cfile_push_chdir(CF_TYPE_VOICE_TRAINING);
else
z = cfile_push_chdir(CF_TYPE_VOICE_BRIEFINGS);
CFileDialog dlg(TRUE, "wav", NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR,
"Voice Files (*.ogg, *.wav)|*.ogg;*.wav|Ogg Vorbis Files (*.ogg)|*.ogg|Wave Files (*.wav)|*.wav||");
if (dlg.DoModal() == IDOK) {
m_voice = dlg.GetFileName();
UpdateData(FALSE);
}
if (!z)
cfile_pop_dir();
}
void briefing_editor_dlg::OnAddStage()
{
int i;
if (Briefing->num_stages >= MAX_BRIEF_STAGES)
return;
m_cur_stage = i = Briefing->num_stages++;
copy_stage(i - 1, i);
audiostream_close_file(m_voice_id, 0);
m_voice_id = -1;
update_data(1);
}
void briefing_editor_dlg::OnDeleteStage()
{
int i, z;
if (m_cur_stage < 0)
return;
audiostream_close_file(m_voice_id, 0);
m_voice_id = -1;
Assert(Briefing->num_stages);
z = m_cur_stage;
m_cur_stage = -1;
update_data(1);
for (i=z+1; i<Briefing->num_stages; i++) {
copy_stage(i, i-1);
}
Briefing->num_stages--;
m_cur_stage = z;
if (m_cur_stage >= Briefing->num_stages)
m_cur_stage = Briefing->num_stages - 1;
update_data(0);
}
void briefing_editor_dlg::draw_icon(object *objp)
{
if (m_cur_stage < 0)
return;
// average the w and h scale factors
float scale_factor = 0.5f * ((float)True_rw / Briefing_window_resolution[0] + (float)True_rh / Briefing_window_resolution[1]);
brief_render_icon(m_cur_stage, objp->instance, 1.0f/30.0f, objp->flags[Object::Object_Flags::Marked], scale_factor);
}
void briefing_editor_dlg::batch_render()
{
int i, num_lines;
if (m_cur_stage < 0)
return;
num_lines = Briefing->stages[m_cur_stage].num_lines;
for (i=0; i<num_lines; i++)
brief_render_icon_line(m_cur_stage, i);
}
void briefing_editor_dlg::icon_select(int num)
{
m_cur_icon = num;
update_data(1);
}
void briefing_editor_dlg::OnInsertStage()
{
int i, z;
if (Briefing->num_stages >= MAX_BRIEF_STAGES)
return;
if (!Briefing->num_stages) {
OnAddStage();
return;
}
audiostream_close_file(m_voice_id, 0);
m_voice_id = -1;
z = m_cur_stage;
m_cur_stage = -1;
update_data(1);
for (i=Briefing->num_stages; i>z; i--) {
copy_stage(i-1, i);
}
Briefing->num_stages++;
copy_stage(z, z + 1);
m_cur_stage = z;
update_data(0);
}
void briefing_editor_dlg::copy_stage(int from, int to)
{
if ((from < 0) || (from >= Briefing->num_stages)) {
Briefing->stages[to].text = "<Text here>";
strcpy_s(Briefing->stages[to].voice, "none.wav");
Briefing->stages[to].camera_pos = view_pos;
Briefing->stages[to].camera_orient = view_orient;
Briefing->stages[to].camera_time = 500;
Briefing->stages[to].num_icons = 0;
Briefing->stages[to].formula = Locked_sexp_true;
Briefing->stages[to].draw_grid = true;
Briefing->stages[to].grid_color = Color_briefing_grid;
return;
}
// Copy all the data in the stage structure.
Briefing->stages[to].text = Briefing->stages[from].text;
strcpy_s( Briefing->stages[to].voice, Briefing->stages[from].voice );
Briefing->stages[to].camera_pos = Briefing->stages[from].camera_pos;
Briefing->stages[to].camera_orient = Briefing->stages[from].camera_orient;
Briefing->stages[to].camera_time = Briefing->stages[from].camera_time;
Briefing->stages[to].flags = Briefing->stages[from].flags;
Briefing->stages[to].num_icons = Briefing->stages[from].num_icons;
Briefing->stages[to].num_lines = Briefing->stages[from].num_lines;
Briefing->stages[to].formula = Briefing->stages[from].formula;
Briefing->stages[to].draw_grid = Briefing->stages[from].draw_grid;
// For now let's just always set this back to default. Eventually when we have a UI color picker in qtFRED, we can copy from stage to stage
Briefing->stages[to].grid_color = Color_briefing_grid;
memmove( Briefing->stages[to].icons, Briefing->stages[from].icons, sizeof(brief_icon)*MAX_STAGE_ICONS );
memmove( Briefing->stages[to].lines, Briefing->stages[from].lines, sizeof(brief_line)*MAX_BRIEF_STAGE_LINES );
}
void briefing_editor_dlg::update_positions()
{
int i, s, z;
vec3d v1, v2;
if (m_cur_stage < 0)
return;
for (i=0; i<Briefing->stages[m_cur_stage].num_icons; i++) {
v1 = Briefing->stages[m_cur_stage].icons[i].pos;
v2 = Objects[icon_obj[i]].pos;
if ((v1.xyz.x != v2.xyz.x) || (v1.xyz.y != v2.xyz.y) || (v1.xyz.z != v2.xyz.z)) {
Briefing->stages[m_cur_stage].icons[i].pos = Objects[icon_obj[i]].pos;
if (!m_change_local) // propagate changes through rest of stages..
for (s=m_cur_stage+1; s<Briefing->num_stages; s++) {
z = find_icon(Briefing->stages[m_cur_stage].icons[i].id, s);
if (z >= 0)
Briefing->stages[s].icons[z].pos = Objects[icon_obj[i]].pos;
}
}
}
}
void briefing_editor_dlg::OnMakeIcon()
{
const char *name;
int team, ship, waypoint, count = -1;
int cargo = 0, cargo_count = 0, freighter_count = 0;
object *ptr;
vec3d min, max, pos;
brief_icon *biconp;
CJumpNode *jnp = nullptr;
if (Briefing->stages[m_cur_stage].num_icons >= MAX_STAGE_ICONS)
return;
m_cur_icon = Briefing->stages[m_cur_stage].num_icons++;
biconp = &Briefing->stages[m_cur_stage].icons[m_cur_icon];
ship = waypoint = -1;
team = 0;
vm_vec_make(&min, 9e19f, 9e19f, 9e19f);
vm_vec_make(&max, -9e19f, -9e19f, -9e19f);
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if (ptr->flags[Object::Object_Flags::Marked]) {
if (ptr->pos.xyz.x < min.xyz.x)
min.xyz.x = ptr->pos.xyz.x;
if (ptr->pos.xyz.x > max.xyz.x)
max.xyz.x = ptr->pos.xyz.x;
if (ptr->pos.xyz.y < min.xyz.y)
min.xyz.y = ptr->pos.xyz.y;
if (ptr->pos.xyz.y > max.xyz.y)
max.xyz.y = ptr->pos.xyz.y;
if (ptr->pos.xyz.z < min.xyz.z)
min.xyz.z = ptr->pos.xyz.z;
if (ptr->pos.xyz.z > max.xyz.z)
max.xyz.z = ptr->pos.xyz.z;
switch (ptr->type) {
case OBJ_SHIP:
case OBJ_START:
ship = ptr->instance;
break;
case OBJ_WAYPOINT:
waypoint = ptr->instance;
break;
case OBJ_JUMP_NODE:
jnp = jumpnode_get_by_objnum(OBJ_INDEX(ptr));
break;
default:
Int3();
}
if (ship >= 0) {
team = Ships[ship].team;
if (Ship_info[Ships[ship].ship_info_index].flags[Ship::Info_Flags::Cargo])
cargo_count++;
if (Ship_info[Ships[ship].ship_info_index].flags[Ship::Info_Flags::Freighter])
{
// direct docked with any marked cargo?
for (dock_instance *dock_ptr = ptr->dock_list; dock_ptr != NULL; dock_ptr = dock_ptr->next)
{
if (dock_ptr->docked_objp->flags[Object::Object_Flags::Marked])
{
if (Ship_info[Ships[dock_ptr->docked_objp->instance].ship_info_index].flags[Ship::Info_Flags::Cargo])
freighter_count++;
}
}
}
}
count++;
}
ptr = GET_NEXT(ptr);
}
if (cargo_count && cargo_count == freighter_count)
cargo = 1;
vm_vec_avg(&pos, &min, &max);
if (ship >= 0)
name = Ships[ship].ship_name;
else if (waypoint >= 0)
{
waypoint_list *wp_list = find_waypoint_list_with_instance(waypoint);
Assert(wp_list != NULL);
name = wp_list->get_name();
}
else if (jnp != nullptr)
name = jnp->GetName();
// Return if we didn't find anything somehow
else
return;
auto len = strlen(name);
if (len >= MAX_LABEL_LEN - 1)
len = MAX_LABEL_LEN - 1;
strncpy(biconp->label, name, len);
biconp->label[len] = 0;
biconp->closeup_label[0] = 0;
// iconp->text[0] = 0;
biconp->type = 0;
biconp->team = team;
biconp->pos = pos;
biconp->flags = 0;
biconp->id = get_unused_briefing_icon_id();
biconp->scale_factor = 1.0f;
biconp->modelnum = -1;
biconp->model_instance_num = -1;
if (ship >= 0) {
biconp->ship_class = Ships[ship].ship_info_index;
ship_info* sip = &Ship_info[Ships[ship].ship_info_index];
if (sip->flags[Ship::Info_Flags::Knossos_device])
biconp->type = ICON_KNOSSOS_DEVICE;
else if (sip->flags[Ship::Info_Flags::Corvette])
biconp->type = ICON_CORVETTE;
else if (sip->flags[Ship::Info_Flags::Gas_miner])
biconp->type = ICON_GAS_MINER;
else if (sip->flags[Ship::Info_Flags::Supercap])
biconp->type = ICON_SUPERCAP;
else if (sip->flags[Ship::Info_Flags::Sentrygun])
biconp->type = ICON_SENTRYGUN;
else if (sip->flags[Ship::Info_Flags::Awacs])
biconp->type = ICON_AWACS;
else if (sip->flags[Ship::Info_Flags::Cargo])
if (cargo)
biconp->type = (count == 1) ? ICON_FREIGHTER_WITH_CARGO : ICON_FREIGHTER_WING_WITH_CARGO;
else
biconp->type = count ? ICON_CARGO_WING : ICON_CARGO;
else if (sip->flags[Ship::Info_Flags::Support])
biconp->type = ICON_SUPPORT_SHIP;
else if (sip->flags[Ship::Info_Flags::Fighter])
biconp->type = count ? ICON_FIGHTER_WING : ICON_FIGHTER;
else if (sip->flags[Ship::Info_Flags::Bomber])
biconp->type = count ? ICON_BOMBER_WING : ICON_BOMBER;
else if (sip->flags[Ship::Info_Flags::Freighter])
if (cargo)
biconp->type = (count == 1) ? ICON_FREIGHTER_WITH_CARGO : ICON_FREIGHTER_WING_WITH_CARGO;
else
biconp->type = count ? ICON_FREIGHTER_WING_NO_CARGO : ICON_FREIGHTER_NO_CARGO;
else if (sip->flags[Ship::Info_Flags::Cruiser])
biconp->type = count ? ICON_CRUISER_WING : ICON_CRUISER;
else if (sip->flags[Ship::Info_Flags::Transport])
biconp->type = count ? ICON_TRANSPORT_WING : ICON_TRANSPORT;
else if (sip->flags[Ship::Info_Flags::Capital] || sip->flags[Ship::Info_Flags::Drydock])
biconp->type = ICON_CAPITAL;
else if (sip->flags[Ship::Info_Flags::Navbuoy])
biconp->type = ICON_WAYPOINT;
else
biconp->type = ICON_ASTEROID_FIELD;
}
// jumpnodes -- have to use Navbuoy if clicked on during briefing because jump nodes are not in ship_info
else if (jnp != nullptr) {
// find the first navbuoy, by iterating through the ship classes
biconp->ship_class = -1;
for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it)
{
if (it->flags[Ship::Info_Flags::Navbuoy])
{
biconp->ship_class = (int)std::distance(Ship_info.cbegin(), it);
break;
}
}
biconp->type = ICON_JUMP_NODE;
}
// everything else -- have to use Navbuoy if clicked on during briefing because waypoints etc. are not in ship_info
else {
// find the first navbuoy
biconp->ship_class = -1;
for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it)
{
if (it->flags[Ship::Info_Flags::Navbuoy])
{
biconp->ship_class = (int)std::distance(Ship_info.cbegin(), it);
break;
}
}
biconp->type = ICON_WAYPOINT;
}
if (!m_change_local){
propagate_icon(m_cur_icon);
}
flagset<Object::Object_Flags> default_flags;
default_flags.set(Object::Object_Flags::Renders);
icon_obj[m_cur_icon] = obj_create(OBJ_POINT, -1, m_cur_icon, NULL, &pos, 0.0f, default_flags);
Assert(icon_obj[m_cur_icon] >= 0);
obj_merge_created_list();
unmark_all();
set_cur_object_index(icon_obj[m_cur_icon]);
GetDlgItem(IDC_MAKE_ICON) -> EnableWindow(FALSE);
GetDlgItem(IDC_PROPAGATE_ICONS) -> EnableWindow(TRUE);
update_data(1);
}
void briefing_editor_dlg::OnDeleteIcon()
{
delete_icon(m_cur_icon);
}
void briefing_editor_dlg::delete_icon(int num)
{
int i, l, z;
if (num < 0)
num = m_cur_icon;
if (num < 0)
return;
Assert(m_cur_stage >= 0);
Assert(Briefing->stages[m_cur_stage].num_icons);
z = m_cur_icon;
if (z == num)
z = -1;
if (z > num)
z--;
m_cur_icon = -1;
update_data(1);
obj_delete(icon_obj[num]);
// remove any lines that reference the icon being deleted
i = Briefing->stages[m_cur_stage].num_lines;
while (i--)
if ((Briefing->stages[m_cur_stage].lines[i].start_icon == num) || (Briefing->stages[m_cur_stage].lines[i].end_icon == num)) {
Briefing->stages[m_cur_stage].num_lines--;
for (l=i; l<Briefing->stages[m_cur_stage].num_lines; l++)
Briefing->stages[m_cur_stage].lines[l] = Briefing->stages[m_cur_stage].lines[l+1];
}
// fix the indexes of lines that reference the icons being shifted down
for (i=0; i<Briefing->stages[m_cur_stage].num_lines; i++) {
if (Briefing->stages[m_cur_stage].lines[i].start_icon > num)
Briefing->stages[m_cur_stage].lines[i].start_icon--;
if (Briefing->stages[m_cur_stage].lines[i].end_icon > num)
Briefing->stages[m_cur_stage].lines[i].end_icon--;
}
for (i=num+1; i<Briefing->stages[m_cur_stage].num_icons; i++) {
Briefing->stages[m_cur_stage].icons[i-1] = Briefing->stages[m_cur_stage].icons[i];
icon_obj[i-1] = icon_obj[i];
Objects[icon_obj[i-1]].instance = i - 1;
}
Briefing->stages[m_cur_stage].num_icons--;
if (z >= 0) {
m_cur_icon = z;
update_data(0);
}
}
void briefing_editor_dlg::OnGotoView()
{
if (m_cur_stage < 0)
return;
view_pos = Briefing->stages[m_cur_stage].camera_pos;
view_orient = Briefing->stages[m_cur_stage].camera_orient;
Update_window = 1;
}
void briefing_editor_dlg::OnSaveView()
{
if (m_cur_stage < 0)
return;
Briefing->stages[m_cur_stage].camera_pos = view_pos;
Briefing->stages[m_cur_stage].camera_orient = view_orient;
}
void briefing_editor_dlg::OnSelchangeIconImage()
{
update_data(1);
}
void briefing_editor_dlg::OnSelchangeTeam()
{
update_data(1);
}
void briefing_editor_dlg::OnSelchangeShipType()
{
update_data(1);
}
int briefing_editor_dlg::check_mouse_hit(int x, int y)
{
int i;
brief_icon *ptr;
if (m_cur_stage < 0)
return -1;
for (i=0; i<Briefing->stages[m_cur_stage].num_icons; i++) {
ptr = &Briefing->stages[m_cur_stage].icons[i];
if ((x > ptr->x) && (x < ptr->x + ptr->w) && (y > ptr->y) && (y < ptr->y + ptr->h)) {
return icon_obj[i];
}
}
return -1;
}
void briefing_editor_dlg::OnPropagateIcons()
{
object *ptr;
// commit any pending edits in the icon fields before propagating the icons forward
update_data(1);
ptr = GET_FIRST(&obj_used_list);
while (ptr != END_OF_LIST(&obj_used_list)) {
if ((ptr->type == OBJ_POINT) && (ptr->flags[Object::Object_Flags::Marked])) {
propagate_icon(ptr->instance);
}
ptr = GET_NEXT(ptr);
}
}
void briefing_editor_dlg::propagate_icon(int num)
{
int i, s;
for (s=m_cur_stage+1; s<Briefing->num_stages; s++) {
i = Briefing->stages[s].num_icons;
if (i >= MAX_STAGE_ICONS)
continue;
if (find_icon(Briefing->stages[m_cur_stage].icons[num].id, s) >= 0)
continue; // don't change if icon exists here already.
Briefing->stages[s].icons[i] = Briefing->stages[m_cur_stage].icons[num];
Briefing->stages[s].num_icons++;
}
}
int briefing_editor_dlg::find_icon(int id, int stage)
{
int i;
if (id >= 0)
for (i=0; i<Briefing->stages[stage].num_icons; i++)
if (Briefing->stages[stage].icons[i].id == id)
return i;
return -1;
}
// is the given id already used by any icon in any stage of the current briefing?
bool briefing_editor_dlg::briefing_icon_id_used(int id)
{
for (int s=0; s<Briefing->num_stages; s++)
if (find_icon(id, s) >= 0)
return true;
return false;
}
// find an id that isn't already used by an icon in the current briefing, advancing
// Cur_brief_id past any values that are already taken (e.g. from manual id edits)
int briefing_editor_dlg::get_unused_briefing_icon_id()
{
while (briefing_icon_id_used(Cur_brief_id))
Cur_brief_id++;
return Cur_brief_id++;
}
void briefing_editor_dlg::reset_icon_loop(int stage)
{
stage_loop = stage + 1;
icon_loop = -1;
}
int briefing_editor_dlg::get_next_icon(int id)
{
while (1) {
icon_loop++;
if (icon_loop >= Briefing->stages[stage_loop].num_icons) {
stage_loop++;
if (stage_loop >= Briefing->num_stages)
return 0;
icon_loop = -1;
continue;
}
iconp = &Briefing->stages[stage_loop].icons[icon_loop];
if ((id >= 0) && (iconp->id == id))
return 1;
}
}
BOOL briefing_editor_dlg::OnCommand(WPARAM wParam, LPARAM lParam)
{
int id;
// deal with figuring out menu stuff
id = LOWORD(wParam);
if ( (id >= ID_TEAM_1) && (id < ID_TEAM_3) ) {
m_current_briefing = id - ID_TEAM_1;
// put user back at first stage for this team (or no current stage is there are none).
Briefing = &Briefings[m_current_briefing];
if ( Briefing->num_stages > 0 )
m_cur_stage = 0;
else
m_cur_stage = -1;
update_data(1);
OnGotoView();
return 1;
}
return CDialog::OnCommand(wParam, lParam);
}
void briefing_editor_dlg::OnLines()
{
if (m_lines.GetCheck() == 1)
m_lines.SetCheck(0);
else
m_lines.SetCheck(1);
update_data(1);
}
void briefing_editor_dlg::OnRclickTree(NMHDR* pNMHDR, LRESULT* pResult)
{
m_tree.right_clicked();
*pResult = 0;
}
void briefing_editor_dlg::OnBeginlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
if (m_tree.edit_label(pTVDispInfo->item.hItem) == 1) {
*pResult = 0;
modified = 1;
} else
*pResult = 1;
}
void briefing_editor_dlg::OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
*pResult = m_tree.end_label_edit(pTVDispInfo->item);
}
BOOL briefing_editor_dlg::DestroyWindow()
{
Briefing_dialog = nullptr;
if (m_play_icon) DestroyIcon(m_play_icon);
audiostream_close_file(m_voice_id, 0);
return CDialog::DestroyWindow();
}
void briefing_editor_dlg::OnPlay()
{
GetDlgItem(IDC_VOICE)->GetWindowText(m_voice);
if (m_voice_id >= 0) {
audiostream_close_file(m_voice_id, 0);
m_voice_id = -1;
return;
}
// we use ASF_EVENTMUSIC here so that it will keep the extension in place
m_voice_id = audiostream_open((char *)(LPCSTR) m_voice, ASF_EVENTMUSIC);
if (m_voice_id >= 0) {
audiostream_play(m_voice_id, 1.0f, 0);
}
}
void briefing_editor_dlg::OnCopyView()
{
// TODO: Add your control notification handler code here
m_copy_view_set = 1;
m_copy_view_pos = view_pos;
m_copy_view_orient = view_orient;
}
void briefing_editor_dlg::OnPasteView()
{
// TODO: Add your control notification handler code here
if (m_cur_stage < 0)
return;
if (m_copy_view_set == 0) {
MessageBox("No view set", "Unable to copy view");
} else {
Briefing->stages[m_cur_stage].camera_pos = m_copy_view_pos;
Briefing->stages[m_cur_stage].camera_orient = m_copy_view_orient;
update_data(1);
OnGotoView();
}
}
void briefing_editor_dlg::OnFlipIcon()
{
update_data(1);
}
void briefing_editor_dlg::OnWingIcon()
{
update_data(1);
}
void briefing_editor_dlg::OnCargoIcon()
{
update_data(1);
}