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