mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
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]>
1536 lines
42 KiB
C++
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);
|
|
} |