Files
fs2open.github.com/fred2/wing_editor.cpp
T
Goober5000andClaude Fable 5 ba2505c3ac preserve custom player wing arrival cues (#7160) (#7716)
FRED and qtFRED no longer overwrite a hand-edited arrival cue on the wing
containing the player start.  The cue is editable when the wing has multiple
waves (where it governs the arrival of subsequent waves) and read-only
otherwise.  Waves and threshold are now editable for the single-player
player wing, while the arrival delay remains locked; the arrival location
is editable in both editors.

Both error checkers flag a single-wave player wing with a custom cue as a
potential issue, and the engine now warns and aborts the mission load,
rather than tripping an Assert, if the player start ship was not created
at mission start.

Also fix an unguarded read of Ships[Player_start_shipnum] in both editors.

Co-authored-by: Claude Fable 5 <[email protected]>
2026-09-10 11:22:30 +02:00

1425 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 "MainFrm.h"
#include "FRED.h"
#include "FREDDoc.h"
#include "Management.h"
#include "wing.h"
#include "ai/aigoals.h"
#include "FREDView.h"
#include "missioneditor/common.h"
#include "cfile/cfile.h"
#include "restrictpaths.h"
#include "warpparamsdlg.h"
#include "ship/ship.h"
#define ID_WING_MENU 9000
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
constexpr auto INPUT_THRESHOLD = 0.001f; // smallest increment of input box
constexpr auto INPUT_FORMAT = "%#.3f";
/////////////////////////////////////////////////////////////////////////////
// wing_editor dialog
wing_editor::wing_editor(CWnd* pParent /*=NULL*/)
: CDialog(wing_editor::IDD, pParent)
{
//{{AFX_DATA_INIT(wing_editor)
m_wing_name = _T("");
m_wing_display_name = _T("");
m_wing_squad_filename = _T("");
m_special_ship = -1;
m_waves = 0;
m_threshold = 0;
m_formation = 0; // retail formation (no formation) is -1, but it's the 0-offset in the combo box
m_formation_scale = _T("1.0");
m_arrival_location = -1;
m_departure_location = -1;
m_arrival_delay = 0;
m_departure_delay = 0;
m_reinforcement = FALSE;
m_hotkey = -1;
m_ignore_count = FALSE;
m_arrival_delay_max = 0;
m_arrival_delay_min = 0;
m_arrival_dist = 0;
m_arrival_target = -1;
m_no_arrival_music = FALSE;
m_departure_target = -1;
m_no_arrival_message = FALSE;
m_no_first_wave_message = FALSE;
m_no_arrival_warp = FALSE;
m_no_departure_warp = FALSE;
m_same_arrival_warp_when_docked = FALSE;
m_same_departure_warp_when_docked = FALSE;
m_no_dynamic = FALSE;
//}}AFX_DATA_INIT
modified = 0;
select_sexp_node = -1;
bypass_errors = 0;
}
void wing_editor::DoDataExchange(CDataExchange* pDX)
{
CString str;
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(wing_editor)
DDX_Control(pDX, IDC_DEPARTURE_DELAY_SPIN, m_departure_delay_spin);
DDX_Control(pDX, IDC_ARRIVAL_DELAY_SPIN, m_arrival_delay_spin);
DDX_Control(pDX, IDC_DEPARTURE_TREE, m_departure_tree);
DDX_Control(pDX, IDC_ARRIVAL_TREE, m_arrival_tree);
DDX_Control(pDX, IDC_SPIN_WAVE_THRESHOLD, m_threshold_spin);
DDX_Control(pDX, IDC_SPIN_WAVES, m_waves_spin);
DDX_Text(pDX, IDC_WING_NAME, m_wing_name);
DDX_Text(pDX, IDC_WING_DISPLAY_NAME, m_wing_display_name);
DDX_Text(pDX, IDC_WING_SQUAD_LOGO, m_wing_squad_filename);
DDX_CBIndex(pDX, IDC_WING_SPECIAL_SHIP, m_special_ship);
DDX_CBIndex(pDX, IDC_WING_FORMATION, m_formation);
DDX_Text(pDX, IDC_WING_FORMATION_SCALE, m_formation_scale);
DDX_CBIndex(pDX, IDC_ARRIVAL_LOCATION, m_arrival_location);
DDX_CBIndex(pDX, IDC_DEPARTURE_LOCATION, m_departure_location);
DDX_Check(pDX, IDC_REINFORCEMENT, m_reinforcement);
DDX_CBIndex(pDX, IDC_HOTKEY, m_hotkey);
DDX_Check(pDX, IDC_IGNORE_COUNT, m_ignore_count);
DDX_Text(pDX, IDC_ARRIVAL_DISTANCE, m_arrival_dist);
DDX_CBIndex(pDX, IDC_ARRIVAL_TARGET, m_arrival_target);
DDX_Check(pDX, IDC_NO_ARRIVAL_MUSIC, m_no_arrival_music);
DDX_CBIndex(pDX, IDC_DEPARTURE_TARGET, m_departure_target);
DDX_Check(pDX, IDC_NO_ARRIVAL_MESSAGE, m_no_arrival_message);
DDX_Check(pDX, IDC_NO_FIRST_WAVE_MESSAGE, m_no_first_wave_message);
DDX_Check(pDX, IDC_NO_ARRIVAL_WARP, m_no_arrival_warp);
DDX_Check(pDX, IDC_NO_DEPARTURE_WARP, m_no_departure_warp);
DDX_Check(pDX, IDC_SAME_ARRIVAL_WARP_WHEN_DOCKED, m_same_arrival_warp_when_docked);
DDX_Check(pDX, IDC_SAME_DEPARTURE_WARP_WHEN_DOCKED, m_same_departure_warp_when_docked);
DDX_Check(pDX, IDC_NO_DYNAMIC, m_no_dynamic);
//}}AFX_DATA_MAP
if (pDX->m_bSaveAndValidate) { // get dialog control values
GetDlgItem(IDC_ARRIVAL_DELAY)->GetWindowText(str);
m_arrival_delay = atoi(str);
if (m_arrival_delay < 0)
m_arrival_delay = 0;
GetDlgItem(IDC_DEPARTURE_DELAY)->GetWindowText(str);
m_departure_delay = atoi(str);
if (m_departure_delay < 0)
m_departure_delay = 0;
GetDlgItem(IDC_WING_WAVES)->GetWindowText(str);
m_waves = atoi(str);
if (m_waves < 0)
m_waves = 0;
GetDlgItem(IDC_WING_WAVE_THRESHOLD)->GetWindowText(str);
m_threshold = atoi(str);
if (m_threshold < 0)
m_threshold = 0;
GetDlgItem(IDC_ARRIVAL_DELAY_MIN)->GetWindowText(str);
m_arrival_delay_min = atoi(str);
if (m_arrival_delay_min < 0)
m_arrival_delay_min = 0;
GetDlgItem(IDC_ARRIVAL_DELAY_MAX)->GetWindowText(str);
m_arrival_delay_max = atoi(str);
if (m_arrival_delay_max < 0)
m_arrival_delay_max = 0;
} else {
DDX_Text(pDX, IDC_ARRIVAL_DELAY, m_arrival_delay);
DDX_Text(pDX, IDC_DEPARTURE_DELAY, m_departure_delay);
DDX_Text(pDX, IDC_WING_WAVES, m_waves);
DDX_Text(pDX, IDC_WING_WAVE_THRESHOLD, m_threshold);
DDX_Text(pDX, IDC_ARRIVAL_DELAY_MIN, m_arrival_delay_min);
DDX_Text(pDX, IDC_ARRIVAL_DELAY_MAX, m_arrival_delay_max);
}
}
BEGIN_MESSAGE_MAP(wing_editor, CDialog)
//{{AFX_MSG_MAP(wing_editor)
ON_WM_INITMENU()
ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_WAVES, OnDeltaposSpinWaves)
ON_NOTIFY(NM_RCLICK, IDC_ARRIVAL_TREE, OnRclickArrivalTree)
ON_NOTIFY(NM_RCLICK, IDC_DEPARTURE_TREE, OnRclickDepartureTree)
ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_ARRIVAL_TREE, OnBeginlabeleditArrivalTree)
ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_DEPARTURE_TREE, OnBeginlabeleditDepartureTree)
ON_NOTIFY(TVN_ENDLABELEDIT, IDC_ARRIVAL_TREE, OnEndlabeleditArrivalTree)
ON_NOTIFY(TVN_ENDLABELEDIT, IDC_DEPARTURE_TREE, OnEndlabeleditDepartureTree)
ON_BN_CLICKED(IDC_DELETE_WING, OnDeleteWing)
ON_BN_CLICKED(IDC_DISBAND_WING, OnDisbandWing)
ON_WM_CLOSE()
ON_BN_CLICKED(IDC_GOALS2, OnGoals2)
ON_BN_CLICKED(IDC_REINFORCEMENT, OnReinforcement)
ON_BN_CLICKED(IDC_NEXT, OnNext)
ON_NOTIFY(TVN_SELCHANGED, IDC_ARRIVAL_TREE, OnSelchangedArrivalTree)
ON_NOTIFY(TVN_SELCHANGED, IDC_DEPARTURE_TREE, OnSelchangedDepartureTree)
ON_BN_CLICKED(IDC_HIDE_CUES, OnHideCues)
ON_BN_CLICKED(IDC_PREV, OnPrev)
ON_CBN_SELCHANGE(IDC_ARRIVAL_LOCATION, OnSelchangeArrivalLocation)
ON_CBN_SELCHANGE(IDC_DEPARTURE_LOCATION, OnSelchangeDepartureLocation)
ON_CBN_SELCHANGE(IDC_HOTKEY, OnSelchangeHotkey)
ON_BN_CLICKED(IDC_WING_SQUAD_LOGO_BUTTON, OnSquadLogo)
ON_BN_CLICKED(IDC_RESTRICT_ARRIVAL, OnRestrictArrival)
ON_BN_CLICKED(IDC_RESTRICT_DEPARTURE, OnRestrictDeparture)
ON_BN_CLICKED(IDC_CUSTOM_WARPIN_PARAMS, OnBnClickedCustomWarpinParams)
ON_BN_CLICKED(IDC_CUSTOM_WARPOUT_PARAMS, OnBnClickedCustomWarpoutParams)
ON_BN_CLICKED(IDC_WING_FORMATION_ALIGN, OnWingFormationAlign)
ON_EN_CHANGE(IDC_WING_NAME, OnChangeWingName)
ON_EN_CHANGE(IDC_WING_WAVES, OnChangeWingWaves)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// wing_editor message handlers
BOOL wing_editor::Create()
{
BOOL r;
int i;
CComboBox *box;
r = CDialog::Create(IDD, Fred_main_wnd);
box = (CComboBox *)GetDlgItem(IDC_WING_FORMATION);
box->ResetContent();
box->AddString("Default");
for (auto &f : Wing_formations)
box->AddString(f.name);
box = (CComboBox *) GetDlgItem(IDC_ARRIVAL_LOCATION);
box->ResetContent();
for (i=0; i<MAX_ARRIVAL_NAMES; i++)
box->AddString(Arrival_location_names[i]);
box = (CComboBox *) GetDlgItem(IDC_DEPARTURE_LOCATION);
box->ResetContent();
for (i=0; i<MAX_DEPARTURE_NAMES; i++)
box->AddString(Departure_location_names[i]);
m_hotkey = 0;
m_waves_spin.SetRange(1, 99);
m_arrival_tree._model.modified = &modified; // provide way to indicate trees are modified in dialog
m_arrival_tree.setup((CEdit *) GetDlgItem(IDC_HELP_BOX));
m_departure_tree._model.modified = &modified;
m_departure_tree.setup();
m_arrival_delay_spin.SetRange(0, 999);
m_departure_delay_spin.SetRange(0, 999);
initialize_data(1);
return r;
}
void wing_editor::OnInitMenu(CMenu* pMenu)
{
CMenu *m;
m = pMenu->GetSubMenu(0);
clear_menu(m);
generate_wing_popup_menu(m, ID_WING_MENU, MF_ENABLED);
if (cur_wing != -1)
m->CheckMenuItem(ID_WING_MENU + cur_wing, MF_BYCOMMAND | MF_CHECKED);
CDialog::OnInitMenu(pMenu);
}
void wing_editor::OnOK()
{
HWND h;
CWnd *w;
w = GetFocus();
if (w) {
h = w->m_hWnd;
GetDlgItem(IDC_ARRIVAL_TREE)->SetFocus();
::SetFocus(h);
}
}
void wing_editor::OnClose()
{
if (verify() && (!bypass_errors)) {
SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
bypass_errors = 0;
return;
}
if (update_data()) {
SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
bypass_errors = 0;
return;
}
SetWindowPos(Fred_main_wnd, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW);
Fred_main_wnd->SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
FREDDoc_ptr->autosave("wing editor");
}
// initialize everything that update_data_safe() saves.
void wing_editor::initialize_data_safe(int full_update)
{
int i, enable = TRUE, player_wing = 0, player_enabled = 1, waves_enabled = 1;
CComboBox *arrival_box, *departure_box;
nprintf(("Fred routing", "Wing dialog load safe\n"));
if (!GetSafeHwnd())
return;
arrival_box = (CComboBox *) GetDlgItem(IDC_ARRIVAL_TARGET);
departure_box = (CComboBox *)GetDlgItem(IDC_DEPARTURE_TARGET);
m_ignore_count = 0;
if (cur_wing < 0) {
m_wing_squad_filename = _T("");
m_wing_display_name = _T("");
m_special_ship = -1;
m_formation = 0;
m_formation_scale = _T("1.0");
m_arrival_location = -1;
m_departure_location = -1;
m_arrival_delay = 0;
m_arrival_delay_min = 0;
m_arrival_delay_max = 0;
m_arrival_dist = 0;
m_arrival_target = -1;
m_departure_target = -1;
m_departure_delay = 0;
m_arrival_tree.clear_tree();
m_departure_tree.clear_tree();
m_arrival_tree.DeleteAllItems();
m_departure_tree.DeleteAllItems();
m_reinforcement = FALSE;
m_hotkey = 0;
m_ignore_count = 0;
m_no_arrival_music = FALSE;
m_no_arrival_message = FALSE;
m_no_first_wave_message = FALSE;
m_no_arrival_warp = FALSE;
m_no_departure_warp = FALSE;
m_same_arrival_warp_when_docked = FALSE;
m_same_departure_warp_when_docked = FALSE;
m_no_dynamic = 0;
waves_enabled = player_enabled = enable = FALSE;
} else {
CComboBox *ptr;
if ((Player_start_shipnum >= 0) && (Player_start_shipnum < MAX_SHIPS) && (Ships[Player_start_shipnum].objnum >= 0))
if (Ships[Player_start_shipnum].wingnum == cur_wing)
player_wing = 1;
if (The_mission.game_type & MISSION_TYPE_MULTI)
{
if (The_mission.game_type & MISSION_TYPE_MULTI_TEAMS)
{
for (i=0; i<MAX_TVT_WINGS; i++)
{
if (cur_wing == TVT_wings[i])
waves_enabled = player_enabled = 0;
}
}
else
{
for (i=0; i<MAX_STARTING_WINGS; i++)
{
if (cur_wing == Starting_wings[i])
waves_enabled = player_enabled = 0;
}
}
}
else
{
// single-player wings, including the player wing, can have multiple waves
if (player_wing)
player_enabled = 0;
}
m_wing_squad_filename = _T(Wings[cur_wing].wing_squad_filename);
m_wing_display_name = Wings[cur_wing].has_display_name() ? Wings[cur_wing].get_display_name() : "<none>";
m_special_ship = Wings[cur_wing].special_ship;
m_waves = Wings[cur_wing].num_waves;
m_threshold = Wings[cur_wing].threshold;
m_formation = Wings[cur_wing].formation + 1;
// there doesn't seem to be an easier way to do this
m_formation_scale.Format(INPUT_FORMAT, Wings[cur_wing].formation_scale);
m_formation_scale.TrimRight('0');
if (m_formation_scale.Right(1) == CString("."))
m_formation_scale.Append("0");
m_arrival_location = static_cast<int>(Wings[cur_wing].arrival_location);
m_departure_location = static_cast<int>(Wings[cur_wing].departure_location);
m_arrival_delay = Wings[cur_wing].arrival_delay;
m_arrival_delay_min = Wings[cur_wing].wave_delay_min;
m_arrival_delay_max = Wings[cur_wing].wave_delay_max;
m_arrival_dist = Wings[cur_wing].arrival_distance;
m_arrival_target = anchor_to_target(Wings[cur_wing].arrival_anchor);
m_departure_target = anchor_to_target(Wings[cur_wing].departure_anchor);
m_no_dynamic = (Wings[cur_wing].flags[Ship::Wing_Flags::No_dynamic])?1:0;
// Add the ships/special items to the combo box here before data is updated
if ( m_arrival_location == static_cast<int>(ArrivalLocation::FROM_DOCK_BAY) ) {
management_add_ships_to_combo( arrival_box, SHIPS_2_COMBO_DOCKING_BAY_ONLY );
} else {
management_add_ships_to_combo( arrival_box, SHIPS_2_COMBO_SPECIAL | SHIPS_2_COMBO_ALL_SHIPS );
}
// Goober5000 - gah, this is ridiculous! Prior to this point in the function (and only in this function),
// m_arrival_target seems to refer to the arrival anchor. The rest of the time, it refers to the index
// of the drop-down list.
if (m_arrival_target >= 0)
{
if (m_arrival_target & ANCHOR_SPECIAL_ARRIVAL)
{
// figure out what the box represents this as
char tmp[NAME_LENGTH + 15];
stuff_special_arrival_anchor_name(tmp, m_arrival_target, false);
// find it in the box
m_arrival_target = arrival_box->FindStringExact(-1, tmp);
}
else
{
// find it in the box
m_arrival_target = arrival_box->FindStringExact(-1, Ships[m_arrival_target].ship_name);
}
}
// add the ships to the departure target combo box
if ( m_departure_location == static_cast<int>(DepartureLocation::TO_DOCK_BAY) ) {
management_add_ships_to_combo( departure_box, SHIPS_2_COMBO_DOCKING_BAY_ONLY );
} else {
departure_box->ResetContent();
}
if ( m_departure_target >= 0 )
m_departure_target = departure_box->FindStringExact(-1, Ships[m_departure_target].ship_name);
m_departure_delay = Wings[cur_wing].departure_delay;
m_arrival_tree.load_tree(Wings[cur_wing].arrival_cue);
m_departure_tree.load_tree(Wings[cur_wing].departure_cue, "false");
m_hotkey = Wings[cur_wing].hotkey+1;
if (Wings[cur_wing].flags[Ship::Wing_Flags::Ignore_count])
m_ignore_count = 1;
else
m_ignore_count = 0;
m_no_arrival_music = Wings[cur_wing].flags[Ship::Wing_Flags::No_arrival_music] ? TRUE : FALSE;
m_no_arrival_message = Wings[cur_wing].flags[Ship::Wing_Flags::No_arrival_message] ? TRUE : FALSE;
m_no_first_wave_message = Wings[cur_wing].flags[Ship::Wing_Flags::No_first_wave_message] ? TRUE : FALSE;
m_no_arrival_warp = Wings[cur_wing].flags[Ship::Wing_Flags::No_arrival_warp] ? TRUE : FALSE;
m_no_departure_warp = Wings[cur_wing].flags[Ship::Wing_Flags::No_departure_warp] ? TRUE : FALSE;
m_same_arrival_warp_when_docked = Wings[cur_wing].flags[Ship::Wing_Flags::Same_arrival_warp_when_docked] ? TRUE : FALSE;
m_same_departure_warp_when_docked = Wings[cur_wing].flags[Ship::Wing_Flags::Same_departure_warp_when_docked] ? TRUE : FALSE;
ptr = (CComboBox *) GetDlgItem(IDC_WING_SPECIAL_SHIP);
ptr->ResetContent();
for (i=0; i<Wings[cur_wing].wave_count; i++)
ptr->AddString(Ships[Wings[cur_wing].ship_index[i]].ship_name);
m_threshold_spin.SetRange(0, static_cast<short>(calc_max_wave_treshold()));
i = find_item_with_string(Reinforcements, &reinforcements::name, Wings[cur_wing].name);
if (i >= 0)
m_reinforcement = TRUE;
else
m_reinforcement = FALSE;
}
if (full_update)
UpdateData(FALSE);
GetDlgItem(IDC_WING_NAME)->EnableWindow(enable);
GetDlgItem(IDC_WING_SQUAD_LOGO_BUTTON)->EnableWindow(enable);
GetDlgItem(IDC_WING_SPECIAL_SHIP)->EnableWindow(enable);
GetDlgItem(IDC_WING_WAVES)->EnableWindow(waves_enabled);
GetDlgItem(IDC_WING_WAVE_THRESHOLD)->EnableWindow(waves_enabled);
GetDlgItem(IDC_DISBAND_WING)->EnableWindow(enable);
GetDlgItem(IDC_SPIN_WAVES)->EnableWindow(waves_enabled);
GetDlgItem(IDC_SPIN_WAVE_THRESHOLD)->EnableWindow(waves_enabled);
GetDlgItem(IDC_WING_FORMATION)->EnableWindow(enable);
GetDlgItem(IDC_WING_FORMATION_ALIGN)->EnableWindow(enable);
GetDlgItem(IDC_WING_FORMATION_SCALE)->EnableWindow(enable);
GetDlgItem(IDC_ARRIVAL_LOCATION)->EnableWindow(enable);
GetDlgItem(IDC_ARRIVAL_DELAY)->EnableWindow(player_enabled);
GetDlgItem(IDC_ARRIVAL_DELAY_MIN)->EnableWindow(player_enabled);
GetDlgItem(IDC_ARRIVAL_DELAY_MAX)->EnableWindow(player_enabled);
GetDlgItem(IDC_ARRIVAL_DELAY_SPIN)->EnableWindow(player_enabled);
if (m_arrival_location) {
GetDlgItem(IDC_ARRIVAL_DISTANCE)->EnableWindow(enable);
GetDlgItem(IDC_ARRIVAL_TARGET)->EnableWindow(enable);
} else {
GetDlgItem(IDC_ARRIVAL_DISTANCE)->EnableWindow(FALSE);
GetDlgItem(IDC_ARRIVAL_TARGET)->EnableWindow(FALSE);
}
if (m_arrival_location == static_cast<int>(ArrivalLocation::FROM_DOCK_BAY)) {
GetDlgItem(IDC_RESTRICT_ARRIVAL)->EnableWindow(enable);
GetDlgItem(IDC_CUSTOM_WARPIN_PARAMS)->EnableWindow(FALSE);
} else {
GetDlgItem(IDC_RESTRICT_ARRIVAL)->EnableWindow(FALSE);
GetDlgItem(IDC_CUSTOM_WARPIN_PARAMS)->EnableWindow(enable);
}
GetDlgItem(IDC_NO_DYNAMIC)->EnableWindow(enable);
if (m_departure_location) {
GetDlgItem(IDC_DEPARTURE_TARGET)->EnableWindow(enable);
} else {
GetDlgItem(IDC_DEPARTURE_TARGET)->EnableWindow(FALSE);
}
if (m_departure_location == static_cast<int>(DepartureLocation::TO_DOCK_BAY)) {
GetDlgItem(IDC_RESTRICT_DEPARTURE)->EnableWindow(enable);
GetDlgItem(IDC_CUSTOM_WARPOUT_PARAMS)->EnableWindow(FALSE);
} else {
GetDlgItem(IDC_RESTRICT_DEPARTURE)->EnableWindow(FALSE);
GetDlgItem(IDC_CUSTOM_WARPOUT_PARAMS)->EnableWindow(enable);
}
// the player wing must be present at mission start, so its arrival cue is only editable
// if it has multiple waves (where the cue governs the arrival of subsequent waves)
if (player_wing)
GetDlgItem(IDC_ARRIVAL_TREE)->EnableWindow(m_waves > 1);
else
GetDlgItem(IDC_ARRIVAL_TREE)->EnableWindow(enable);
GetDlgItem(IDC_DEPARTURE_LOCATION)->EnableWindow(enable);
GetDlgItem(IDC_DEPARTURE_DELAY)->EnableWindow(enable);
GetDlgItem(IDC_DEPARTURE_DELAY_SPIN)->EnableWindow(enable);
GetDlgItem(IDC_DEPARTURE_TREE)->EnableWindow(enable);
GetDlgItem(IDC_GOALS2)->EnableWindow(enable);
GetDlgItem(IDC_DELETE_WING)->EnableWindow(enable);
GetDlgItem(IDC_REINFORCEMENT)->EnableWindow(enable);
GetDlgItem(IDC_HOTKEY)->EnableWindow(enable);
GetDlgItem(IDC_IGNORE_COUNT)->EnableWindow(enable);
GetDlgItem(IDC_NO_ARRIVAL_MUSIC)->EnableWindow(enable);
GetDlgItem(IDC_NO_ARRIVAL_MESSAGE)->EnableWindow(enable);
GetDlgItem(IDC_NO_FIRST_WAVE_MESSAGE)->EnableWindow(enable);
GetDlgItem(IDC_NO_ARRIVAL_WARP)->EnableWindow(enable);
GetDlgItem(IDC_NO_DEPARTURE_WARP)->EnableWindow(enable);
GetDlgItem(IDC_SAME_ARRIVAL_WARP_WHEN_DOCKED)->EnableWindow(enable);
GetDlgItem(IDC_SAME_DEPARTURE_WARP_WHEN_DOCKED)->EnableWindow(enable);
if (cur_wing >= 0) {
enable = TRUE;
// check to see if the wing has a ship which is not a fighter/bomber type. If so, then disable
// the wing_waves and wing_threshold stuff
for (i = 0; i < Wings[cur_wing].wave_count; i++ ) {
if ( !(Ship_info[Ships[Wings[cur_wing].ship_index[i]].ship_info_index].is_fighter_bomber() ))
enable = FALSE;
}
} else
enable = FALSE;
}
void wing_editor::initialize_data(int full_update)
{
int i;
CWnd *w = NULL;
nprintf(("Fred routing", "Wing dialog load\n"));
if (!GetSafeHwnd())
return;
m_arrival_tree.select_sexp_node = m_departure_tree.select_sexp_node = select_sexp_node;
select_sexp_node = -1;
if (cur_wing == -1)
m_wing_name = _T("");
else
m_wing_name = _T(Wings[cur_wing].name);
initialize_data_safe(full_update);
modified = 0;
if (w)
w -> SetFocus();
i = m_arrival_tree.select_sexp_node;
if (i != -1) {
w = GetDlgItem(IDC_ARRIVAL_TREE);
m_arrival_tree.hilite_item(i);
} else {
i = m_departure_tree.select_sexp_node;
if (i != -1) {
w = GetDlgItem(IDC_DEPARTURE_TREE);
m_departure_tree.hilite_item(i);
}
}
}
// update wing structure(s) with dialog data. The data is first checked for errors. If
// no errors occur, returns 0. If an error occurs, returns -1. If the update is bypassed,
// returns 1. Bypass is necessary to avoid an infinite loop, and it doesn't actually
// update the data. Bypass only occurs if bypass mode is active and we still get an error.
// Once the error no longer occurs, bypass mode is cleared and data is updated.
int wing_editor::update_data(int redraw)
{
char *str, old_name[255], buf[512];
int i, z;
nprintf(("Fred routing", "Wing dialog save\n"));
if (!GetSafeHwnd())
return 0;
UpdateData(TRUE);
UpdateData(TRUE);
m_wing_name.TrimLeft();
m_wing_name.TrimRight();
if (cur_wing >= 0) {
SCP_string conflict = check_name_conflict("wing", m_wing_name, -1, cur_wing);
if (!conflict.empty()) {
if (bypass_errors)
return 1;
bypass_errors = 1;
CString msg;
msg.Format("%s\nPress OK to restore old name", conflict.c_str());
z = MessageBox(msg, "Error", MB_ICONEXCLAMATION | MB_OKCANCEL);
if (z == IDCANCEL)
return -1;
m_wing_name = _T(Wings[cur_wing].name);
UpdateData(FALSE);
}
strcpy_s(old_name, Wings[cur_wing].name);
string_copy(Wings[cur_wing].name, m_wing_name, NAME_LENGTH, 1);
lcl_fred_replace_stuff(m_wing_display_name);
// the display name was precalculated, so now just assign it
if (m_wing_display_name == m_wing_name || m_wing_display_name.CompareNoCase("<none>") == 0)
{
if (Wings[cur_wing].has_display_name())
set_modified();
Wings[cur_wing].display_name = "";
Wings[cur_wing].flags.remove(Ship::Wing_Flags::Has_display_name);
}
else
{
if (!Wings[cur_wing].has_display_name() || Wings[cur_wing].display_name != (LPCSTR)m_wing_display_name)
set_modified();
Wings[cur_wing].display_name = m_wing_display_name;
Wings[cur_wing].flags.set(Ship::Wing_Flags::Has_display_name);
}
update_data_safe();
update_custom_wing_indexes();
bypass_errors = 0;
modified = 0;
str = Wings[cur_wing].name;
if (strcmp(old_name, str)) {
update_sexp_references(old_name, str);
ai_update_goal_references(sexp_ref_type::WING, old_name, str);
update_texture_replacements(old_name, str);
i = find_item_with_string(Reinforcements, &reinforcements::name, old_name);
if (i >= 0)
strcpy_s(Reinforcements[i].name, str);
for (i=0; i<Wings[cur_wing].wave_count; i++) {
if ((Objects[wing_objects[cur_wing][i]].type == OBJ_SHIP) || (Objects[wing_objects[cur_wing][i]].type == OBJ_START)) {
wing_bash_ship_name(buf, str, i + 1);
rename_ship(Wings[cur_wing].ship_index[i], buf);
// bash it again for the display name
wing_bash_ship_name(&Ships[Wings[cur_wing].ship_index[i]], &Wings[cur_wing], i + 1, true);
}
}
Update_window = 1;
}
// Update as normal.
if (set_reinforcement(str, m_reinforcement) == 1)
{
free_sexp2(Wings[cur_wing].arrival_cue);
Wings[cur_wing].arrival_cue = Locked_sexp_false;
}
}
if (redraw)
update_map_window();
return 0;
}
// update parts of wing that can't fail. This is useful if for when you need to change
// something in a wing that this updates elsewhere in Fred. Normally when auto-update
// kicks in, the changes you make will be wiped out by the auto=update, so instead you
// would call this function to update the wing, make your changes, and then call the
// initialize_data_safe() function to show your changes in the dialog
void wing_editor::update_data_safe()
{
char buf[512];
int i, d, hotkey = -1;
nprintf(("Fred routing", "Wing dialog save safe\n"));
if (!GetSafeHwnd())
return;
UpdateData(TRUE);
UpdateData(TRUE);
if (cur_wing < 0)
return;
if (m_threshold > calc_max_wave_treshold()) {
m_threshold = calc_max_wave_treshold();
if (!bypass_errors) {
sprintf(buf, "Wave threshold is set too high. Value has been lowered to %d", (int) m_threshold);
MessageBox(buf);
}
}
if (m_waves < 1) {
m_waves = 1;
if (!bypass_errors) {
sprintf(buf, "Number of waves illegal. Can not have %d waves. Number of waves has as been set to 1.", (int) m_waves);
MessageBox(buf);
}
}
MODIFY(Wings[cur_wing].special_ship, m_special_ship);
MODIFY(Wings[cur_wing].num_waves, m_waves);
MODIFY(Wings[cur_wing].threshold, m_threshold);
MODIFY(Wings[cur_wing].formation, m_formation - 1);
MODIFY(Wings[cur_wing].formation_scale, (float)atof(m_formation_scale));
MODIFY(Wings[cur_wing].arrival_location, static_cast<ArrivalLocation>(m_arrival_location));
MODIFY(Wings[cur_wing].departure_location, static_cast<DepartureLocation>(m_departure_location));
MODIFY(Wings[cur_wing].arrival_delay, m_arrival_delay);
if (m_arrival_delay_min > m_arrival_delay_max) {
if (!bypass_errors) {
sprintf(buf, "Arrival delay minimum greater than maximum. Value lowered to %d", m_arrival_delay_max);
MessageBox(buf);
}
m_arrival_delay_min = m_arrival_delay_max;
}
MODIFY(Wings[cur_wing].wave_delay_min, m_arrival_delay_min);
MODIFY(Wings[cur_wing].wave_delay_max, m_arrival_delay_max);
MODIFY(Wings[cur_wing].arrival_distance, m_arrival_dist);
if (m_arrival_target >= 0) {
i = (int)((CComboBox *) GetDlgItem(IDC_ARRIVAL_TARGET))->GetItemData(m_arrival_target);
MODIFY(Wings[cur_wing].arrival_anchor, target_to_anchor(i));
// when arriving near or in front of a ship, be sure that we are far enough away from it!!!
if (((m_arrival_location != static_cast<int>(ArrivalLocation::AT_LOCATION)) && (m_arrival_location != static_cast<int>(ArrivalLocation::FROM_DOCK_BAY))) && (i >= 0) && !(i & ANCHOR_SPECIAL_ARRIVAL)) {
d = int(std::min(MIN_TARGET_ARRIVAL_DISTANCE, MIN_TARGET_ARRIVAL_MULTIPLIER * Objects[Ships[i].objnum].radius));
if ((Wings[cur_wing].arrival_distance < d) && (Wings[cur_wing].arrival_distance > -d)) {
if (!bypass_errors) {
sprintf(buf, "Ship must arrive at least %d meters away from target.\n"
"Value has been reset to this. Use with caution!\r\n"
"Recommended distance is %d meters.\r\n", d, (int)(MIN_TARGET_ARRIVAL_MULTIPLIER * Objects[Ships[i].objnum].radius) );
MessageBox(buf);
}
if (Wings[cur_wing].arrival_distance < 0)
Wings[cur_wing].arrival_distance = -d;
else
Wings[cur_wing].arrival_distance = d;
m_arrival_dist = Wings[cur_wing].arrival_distance;
}
}
}
if (m_departure_target >= 0) {
i = (int)((CComboBox *) GetDlgItem(IDC_DEPARTURE_TARGET))->GetItemData(m_departure_target);
MODIFY(Wings[cur_wing].departure_anchor, target_to_anchor(i));
}
MODIFY(Wings[cur_wing].departure_delay, m_departure_delay);
hotkey = m_hotkey - 1;
MODIFY(Wings[cur_wing].hotkey, hotkey);
if (m_ignore_count) {
if (!(Wings[cur_wing].flags[Ship::Wing_Flags::Ignore_count]))
set_modified();
Wings[cur_wing].flags.set(Ship::Wing_Flags::Ignore_count);
}
else {
if (Wings[cur_wing].flags[Ship::Wing_Flags::Ignore_count])
set_modified();
Wings[cur_wing].flags.remove(Ship::Wing_Flags::Ignore_count);
}
if (m_no_arrival_music) {
if (!(Wings[cur_wing].flags[Ship::Wing_Flags::No_arrival_music]))
set_modified();
Wings[cur_wing].flags.set(Ship::Wing_Flags::No_arrival_music);
}
else {
if (Wings[cur_wing].flags[Ship::Wing_Flags::No_arrival_music])
set_modified();
Wings[cur_wing].flags.remove(Ship::Wing_Flags::No_arrival_music);
}
// check the no message flag
if (m_no_arrival_message) {
if (!(Wings[cur_wing].flags[Ship::Wing_Flags::No_arrival_message]))
set_modified();
Wings[cur_wing].flags.set(Ship::Wing_Flags::No_arrival_message);
}
else {
if (Wings[cur_wing].flags[Ship::Wing_Flags::No_arrival_message])
set_modified();
Wings[cur_wing].flags.remove(Ship::Wing_Flags::No_arrival_message);
}
if (m_no_first_wave_message) {
if (!(Wings[cur_wing].flags[Ship::Wing_Flags::No_first_wave_message]))
set_modified();
Wings[cur_wing].flags.set(Ship::Wing_Flags::No_first_wave_message);
}
else {
if (Wings[cur_wing].flags[Ship::Wing_Flags::No_first_wave_message])
set_modified();
Wings[cur_wing].flags.remove(Ship::Wing_Flags::No_first_wave_message);
}
// set the no warp effect for wings flag
if (m_no_arrival_warp) {
if (!(Wings[cur_wing].flags[Ship::Wing_Flags::No_arrival_warp]))
set_modified();
Wings[cur_wing].flags.set(Ship::Wing_Flags::No_arrival_warp);
}
else {
if (Wings[cur_wing].flags[Ship::Wing_Flags::No_arrival_warp])
set_modified();
Wings[cur_wing].flags.remove(Ship::Wing_Flags::No_arrival_warp);
}
// set the no warp effect for wings flag
if (m_no_departure_warp) {
if (!(Wings[cur_wing].flags[Ship::Wing_Flags::No_departure_warp]))
set_modified();
Wings[cur_wing].flags.set(Ship::Wing_Flags::No_departure_warp);
}
else {
if (Wings[cur_wing].flags[Ship::Wing_Flags::No_departure_warp])
set_modified();
Wings[cur_wing].flags.remove(Ship::Wing_Flags::No_departure_warp);
}
// set the same warp effect for wings flag
if (m_same_arrival_warp_when_docked) {
if (!(Wings[cur_wing].flags[Ship::Wing_Flags::Same_arrival_warp_when_docked]))
set_modified();
Wings[cur_wing].flags.set(Ship::Wing_Flags::Same_arrival_warp_when_docked);
}
else {
if (Wings[cur_wing].flags[Ship::Wing_Flags::Same_arrival_warp_when_docked])
set_modified();
Wings[cur_wing].flags.remove(Ship::Wing_Flags::Same_arrival_warp_when_docked);
}
// set the same warp effect for wings flag
if (m_same_departure_warp_when_docked) {
if (!(Wings[cur_wing].flags[Ship::Wing_Flags::Same_departure_warp_when_docked]))
set_modified();
Wings[cur_wing].flags.set(Ship::Wing_Flags::Same_departure_warp_when_docked);
}
else {
if (Wings[cur_wing].flags[Ship::Wing_Flags::Same_departure_warp_when_docked])
set_modified();
Wings[cur_wing].flags.remove(Ship::Wing_Flags::Same_departure_warp_when_docked);
}
if (m_no_dynamic) {
if (!(Wings[cur_wing].flags[Ship::Wing_Flags::No_dynamic]))
set_modified();
Wings[cur_wing].flags.set(Ship::Wing_Flags::No_dynamic);
}
else {
if (Wings[cur_wing].flags[Ship::Wing_Flags::No_dynamic])
set_modified();
Wings[cur_wing].flags.remove(Ship::Wing_Flags::No_dynamic);
}
if (Wings[cur_wing].arrival_cue >= 0)
free_sexp2(Wings[cur_wing].arrival_cue);
Wings[cur_wing].arrival_cue = m_arrival_tree._model.save_tree();
if (Wings[cur_wing].departure_cue >= 0)
free_sexp2(Wings[cur_wing].departure_cue);
Wings[cur_wing].departure_cue = m_departure_tree._model.save_tree();
// copy squad stuff
if(stricmp(m_wing_squad_filename, Wings[cur_wing].wing_squad_filename))
{
string_copy(Wings[cur_wing].wing_squad_filename, m_wing_squad_filename, MAX_FILENAME_LEN - 1);
set_modified();
}
if (modified)
set_modified();
}
BOOL wing_editor::OnCommand(WPARAM wParam, LPARAM lParam)
{
int id, wing;
id = LOWORD(wParam);
if (id >= ID_WING_MENU && id < ID_WING_MENU + MAX_WINGS) {
if (!update_data()) {
wing = id - ID_WING_MENU;
mark_wing(wing);
return 1;
}
}
return CDialog::OnCommand(wParam, lParam);
}
void wing_editor::OnDeltaposSpinWaves(NMHDR* pNMHDR, LRESULT* pResult)
{
int new_pos;
NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR;
new_pos = pNMUpDown->iPos + pNMUpDown->iDelta;
if (new_pos > 0 && new_pos < 100) {
*pResult = 0;
modified = 1;
} else
*pResult = 1;
}
void wing_editor::OnRclickArrivalTree(NMHDR* pNMHDR, LRESULT* pResult)
{
m_arrival_tree.right_clicked();
*pResult = 0;
}
void wing_editor::OnRclickDepartureTree(NMHDR* pNMHDR, LRESULT* pResult)
{
m_departure_tree.right_clicked();
*pResult = 0;
}
void wing_editor::OnBeginlabeleditArrivalTree(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
if (m_arrival_tree.edit_label(pTVDispInfo->item.hItem) == 1) {
*pResult = 0;
modified = 1;
} else
*pResult = 1;
}
void wing_editor::OnBeginlabeleditDepartureTree(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
if (m_departure_tree.edit_label(pTVDispInfo->item.hItem) == 1) {
*pResult = 0;
modified = 1;
} else
*pResult = 1;
}
void wing_editor::OnEndlabeleditArrivalTree(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
*pResult = m_arrival_tree.end_label_edit(pTVDispInfo->item);
}
void wing_editor::OnEndlabeleditDepartureTree(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
*pResult = m_departure_tree.end_label_edit(pTVDispInfo->item);
}
int wing_editor::verify()
{
nprintf(("Fred routing", "Wing dialog verify\n"));
if (!GetSafeHwnd() || !modified)
return 0;
if (bypass_errors)
return 1;
return 0;
}
// delete wing and all ships that are part of the wing
void wing_editor::OnDeleteWing()
{
modified = 0; // no need to run update checks, since wing will be gone shortly anyway.
delete_wing(cur_wing);
}
// delete wing, but leave ships intact and wingless
void wing_editor::OnDisbandWing()
{
modified = 0; // no need to run update checks, since wing will be gone shortly anyway.
disband_wing(cur_wing);
}
void wing_editor::OnGoals2()
{
ShipGoalsDlg dlg_goals;
Assert(cur_wing != -1);
dlg_goals.self_wing = cur_wing;
dlg_goals.DoModal();
if (query_initial_orders_conflict(cur_wing))
MessageBox("One or more ships of this wing also has initial orders",
"Possible conflict");
}
void wing_editor::OnReinforcement()
{
UpdateData(TRUE);
UpdateData(TRUE);
//if (m_reinforcement)
// m_arrival_tree.clear_tree("false");
}
void wing_editor::OnPrev()
{
int wing, count = 0;
if (!update_data() && Num_wings) {
wing = cur_wing - 1;
if (wing < 0)
wing = MAX_WINGS - 1;
while (!Wings[wing].wave_count) {
wing--;
if (count++ > MAX_WINGS)
return;
if (wing < 0)
wing = MAX_WINGS - 1;
}
mark_wing(wing);
Wing_editor_dialog.initialize_data(1);
Update_wing = 0;
}
return;
}
void wing_editor::OnNext()
{
int wing, count = 0;
if (!update_data() && Num_wings) {
wing = cur_wing + 1;
if (wing >= MAX_WINGS)
wing = 0;
while (!Wings[wing].wave_count) {
wing++;
if (count++ > MAX_WINGS)
return;
if (wing >= MAX_WINGS)
wing = 0;
}
mark_wing(wing);
Wing_editor_dialog.initialize_data(1);
Update_wing = 0;
}
return;
}
void wing_editor::OnSelchangedArrivalTree(NMHDR* pNMHDR, LRESULT* pResult)
{
HTREEITEM h;
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
h = pNMTreeView->itemNew.hItem;
if (h)
m_arrival_tree.update_help(h);
*pResult = 0;
}
void wing_editor::OnSelchangedDepartureTree(NMHDR* pNMHDR, LRESULT* pResult)
{
HTREEITEM h;
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
h = pNMTreeView->itemNew.hItem;
if (h)
m_departure_tree.update_help(h);
*pResult = 0;
}
void wing_editor::calc_help_height()
{
CRect minihelp, help;
GetDlgItem(IDC_MINI_HELP_BOX)->GetWindowRect(minihelp);
GetDlgItem(IDC_HELP_BOX)->GetWindowRect(help);
help_height = (help.bottom - minihelp.top) + 10;
}
void wing_editor::calc_cue_height()
{
CRect cue;
GetDlgItem(IDC_CUE_FRAME)->GetWindowRect(cue);
cue_height = (cue.bottom - cue.top) + 1;
}
void wing_editor::show_hide_sexp_help()
{
CRect rect;
if (((CButton *) GetDlgItem(IDC_HIDE_CUES)) -> GetCheck())
return;
GetWindowRect(rect);
if (Show_sexp_help)
rect.bottom += help_height;
else
rect.bottom -= help_height;
MoveWindow(rect);
}
void wing_editor::show_hide_cues()
{
((CButton*)GetDlgItem(IDC_HIDE_CUES))->SetCheck(Hide_wing_cues ? TRUE : FALSE);
OnHideCues();
}
void wing_editor::OnHideCues()
{
CRect rect;
GetWindowRect(rect);
if (((CButton *) GetDlgItem(IDC_HIDE_CUES)) -> GetCheck()) {
rect.bottom -= cue_height;
if (Show_sexp_help)
rect.bottom -= help_height;
Hide_wing_cues = 1;
} else {
rect.bottom += cue_height;
if (Show_sexp_help)
rect.bottom += help_height;
Hide_wing_cues = 0;
}
MoveWindow(rect);
}
void wing_editor::OnSelchangeArrivalLocation()
{
CComboBox *box;
box = (CComboBox *)GetDlgItem(IDC_ARRIVAL_TARGET);
UpdateData();
if (m_arrival_location) {
GetDlgItem(IDC_ARRIVAL_DISTANCE)->EnableWindow(TRUE);
GetDlgItem(IDC_ARRIVAL_TARGET)->EnableWindow(TRUE);
if (m_arrival_target < 0) {
m_arrival_target = 0;
}
// determine which items we should put into the arrival target combo box
if ( m_arrival_location == static_cast<int>(ArrivalLocation::FROM_DOCK_BAY) ) {
management_add_ships_to_combo( box, SHIPS_2_COMBO_DOCKING_BAY_ONLY );
} else {
management_add_ships_to_combo( box, SHIPS_2_COMBO_SPECIAL | SHIPS_2_COMBO_ALL_SHIPS );
}
} else {
m_arrival_target = -1;
GetDlgItem(IDC_ARRIVAL_DISTANCE)->EnableWindow(FALSE);
GetDlgItem(IDC_ARRIVAL_TARGET)->EnableWindow(FALSE);
}
if (m_arrival_location == static_cast<int>(ArrivalLocation::FROM_DOCK_BAY)) {
GetDlgItem(IDC_RESTRICT_ARRIVAL)->EnableWindow(TRUE);
GetDlgItem(IDC_CUSTOM_WARPIN_PARAMS)->EnableWindow(FALSE);
} else {
GetDlgItem(IDC_RESTRICT_ARRIVAL)->EnableWindow(FALSE);
GetDlgItem(IDC_CUSTOM_WARPIN_PARAMS)->EnableWindow(TRUE);
}
UpdateData(FALSE);
}
void wing_editor::OnSelchangeDepartureLocation()
{
CComboBox *box;
box = (CComboBox *)GetDlgItem(IDC_DEPARTURE_TARGET);
UpdateData();
if (m_departure_location) {
GetDlgItem(IDC_DEPARTURE_TARGET)->EnableWindow(TRUE);
if (m_departure_target < 0) {
m_departure_target = 0;
}
// we need to build up the list box content based on the departure type. When
// from a docking bay, only show ships in the list which have them. Show all ships otherwise
if ( m_departure_location == static_cast<int>(DepartureLocation::TO_DOCK_BAY) ) {
management_add_ships_to_combo( box, SHIPS_2_COMBO_DOCKING_BAY_ONLY );
} else {
// I think that this section is currently illegal
Int3();
}
} else {
m_departure_target = -1;
GetDlgItem(IDC_DEPARTURE_TARGET)->EnableWindow(FALSE);
}
if (m_departure_location == static_cast<int>(DepartureLocation::TO_DOCK_BAY)) {
GetDlgItem(IDC_RESTRICT_DEPARTURE)->EnableWindow(TRUE);
GetDlgItem(IDC_CUSTOM_WARPOUT_PARAMS)->EnableWindow(FALSE);
} else {
GetDlgItem(IDC_RESTRICT_DEPARTURE)->EnableWindow(FALSE);
GetDlgItem(IDC_CUSTOM_WARPOUT_PARAMS)->EnableWindow(TRUE);
}
UpdateData(FALSE);
}
// see if hotkey should possibly be reserved for player starting wing
void wing_editor::OnSelchangeHotkey()
{
char buf[256];
int set_num, i;
UpdateData(TRUE);
set_num = m_hotkey - 1; // hotkey sets are 1 index based
// first, determine if we are currently working with a starting wing
for ( i = 0; i < MAX_STARTING_WINGS; i++ ) {
if ( !stricmp( Wings[cur_wing].name, Starting_wing_names[i]) )
break;
}
if ( i == MAX_STARTING_WINGS )
return;
// we have a player starting wing. See if we assigned a non-standard hotkey
if ( (set_num >= MAX_STARTING_WINGS) || (set_num != i) ) {
sprintf(buf, "Assigning nonstandard hotkey to wing %s (default is F%d)", Wings[cur_wing].name, 5+i);
MessageBox(buf, NULL, MB_OK | MB_ICONEXCLAMATION );
}
}
void wing_editor::OnSquadLogo()
{
int z;
char *Logo_ext = "Image Files (*.dds, *.pcx)|*.dds;*.pcx|"
"DDS Files (*.dds)|*.dds|"
"PCX Files (*.pcx)|*.pcx|"
"All Files (*.*)|*.*|"
"|";
//phreak 05/05/2003
//this needs to be here or else the data in the wing editor dialog will revert
//to what it was before it was opened.
UpdateData(TRUE);
// get list of squad images
z = cfile_push_chdir(CF_TYPE_SQUAD_IMAGES);
CFileDialog dlg(TRUE, NULL, NULL, OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR, Logo_ext);
// if we have a result
if (dlg.DoModal() == IDOK) {
m_wing_squad_filename = dlg.GetFileName();
} else {
m_wing_squad_filename = _T("");
}
UpdateData(FALSE);
// restore directory
if (!z){
cfile_pop_dir();
}
}
// Goober5000
void wing_editor::OnRestrictArrival()
{
int arrive_from_ship;
CComboBox *box;
restrict_paths dlg;
// grab stuff from GUI
UpdateData(TRUE);
if (m_arrival_location != static_cast<int>(ArrivalLocation::FROM_DOCK_BAY))
{
Int3();
return;
}
box = (CComboBox *) GetDlgItem(IDC_ARRIVAL_TARGET);
if (box->GetCount() == 0)
return;
arrive_from_ship = (int)box->GetItemData(m_arrival_target);
if (!ship_has_hangar_bay(arrive_from_ship))
{
Int3();
return;
}
dlg.m_arrival = true;
dlg.m_ship_class = Ships[arrive_from_ship].ship_info_index;
dlg.m_path_mask = &Wings[cur_wing].arrival_path_mask;
dlg.DoModal();
}
// Goober5000
void wing_editor::OnRestrictDeparture()
{
int depart_to_ship;
CComboBox *box;
restrict_paths dlg;
// grab stuff from GUI
UpdateData(TRUE);
if (m_departure_location != static_cast<int>(DepartureLocation::TO_DOCK_BAY))
{
Int3();
return;
}
box = (CComboBox *) GetDlgItem(IDC_DEPARTURE_TARGET);
if (box->GetCount() == 0)
return;
depart_to_ship = (int)box->GetItemData(m_departure_target);
if (!ship_has_hangar_bay(depart_to_ship))
{
Int3();
return;
}
dlg.m_arrival = false;
dlg.m_ship_class = Ships[depart_to_ship].ship_info_index;
dlg.m_path_mask = &Wings[cur_wing].departure_path_mask;
dlg.DoModal();
}
int wing_editor::calc_max_wave_treshold()
{
// Maximum MAX_SHIPS_PER_WING ships can be alive at any given time
return std::max(0, MAX_SHIPS_PER_WING - Wings[cur_wing].wave_count);
}
void wing_editor::OnBnClickedCustomWarpinParams()
{
warp_params_dlg dlg;
dlg.m_warp_in = true;
dlg.DoModal();
}
void wing_editor::OnBnClickedCustomWarpoutParams()
{
warp_params_dlg dlg;
dlg.m_warp_in = false;
dlg.DoModal();
}
void wing_editor::OnWingFormationAlign()
{
auto wingp = &Wings[cur_wing];
auto leader_objp = &Objects[Ships[wingp->ship_index[0]].objnum];
// temporarily set formation in case it isn't set yet (since changes don't take effect until dialog is closed)
auto old_formation = wingp->formation;
auto old_formation_scale = wingp->formation_scale;
UpdateData(TRUE); // read controls
wingp->formation = m_formation - 1;
wingp->formation_scale = (float)atof(m_formation_scale);
for (int i = 1; i < wingp->wave_count; i++)
{
auto objp = &Objects[Ships[wingp->ship_index[i]].objnum];
get_absolute_wing_pos(&objp->pos, leader_objp, cur_wing, i, false);
objp->orient = leader_objp->orient;
// drag any docked partners along (no-op for undocked ships)
object_moved(objp);
}
// roll back temporary formation
wingp->formation = old_formation;
wingp->formation_scale = old_formation_scale;
}
void wing_editor::OnChangeWingName()
{
// sync the edit box to the variable
UpdateData(TRUE);
// automatically determine or reset the display name
m_wing_display_name = get_display_name_for_text_box(m_wing_name);
// sync the variable to the edit box
UpdateData(FALSE);
}
void wing_editor::OnChangeWingWaves()
{
if (!GetSafeHwnd() || cur_wing < 0)
return;
// only the player wing's arrival cue is gated on the number of waves
if ((Player_start_shipnum < 0) || (Player_start_shipnum >= MAX_SHIPS) || (Ships[Player_start_shipnum].objnum < 0))
return;
if (Ships[Player_start_shipnum].wingnum != cur_wing)
return;
// read the control directly; this notification can arrive in the middle of a DDX update,
// so calling UpdateData() here would not be safe
CString str;
GetDlgItem(IDC_WING_WAVES)->GetWindowText(str);
GetDlgItem(IDC_ARRIVAL_TREE)->EnableWindow(atoi(str) > 1);
}