Files
fs2open.github.com/fred2/playerstarteditor.cpp
Goober5000andClaude Opus 5 a159e67a10 share the loadout usage-list code between the two editors
Add generate_ship_usage_list_team alongside the weaponry twin that already existed in missioneditor/common, so both loadout editors can drop their TVT/non-TVT branches and simply iterate Num_teams.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-25 03:22:23 -04:00

1114 lines
34 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 "FREDDoc.h"
#include "PlayerStartEditor.h"
#include "mission/missionparse.h"
#include "missioneditor/common.h"
#include "object/object.h"
#include "Management.h"
#include "weapon/weapon.h"
#include "checkboxlistdlg.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// player_start_editor dialog
player_start_editor::player_start_editor(CWnd* pParent) : CDialog(player_start_editor::IDD, pParent)
{
//{{AFX_DATA_INIT(player_start_editor)
m_delay = 0;
m_weapon_pool = 0;
m_ship_pool = 0;
m_validation_toggle = FALSE;
//}}AFX_DATA_INIT
selected_team = 0;
autobalance = false;
previous_team = -1;
dlg_inited = 0;
}
void player_start_editor::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(player_start_editor)
DDX_Control(pDX, IDC_POOL_SPIN, m_pool_spin);
DDX_Control(pDX, IDC_DELAY_SPIN, m_delay_spin);
DDX_Control(pDX, IDC_SPIN1, m_spin1);
DDX_Control(pDX, IDC_SHIP_LIST, m_ship_list);
DDX_Control(pDX, IDC_WEAPON_LIST, m_weapon_list);
DDX_Control(pDX, IDC_SHIP_VARIABLES_LIST, m_ship_variable_list);
DDX_Control(pDX, IDC_WEAPON_VARIABLES_LIST, m_weapon_variable_list);
DDX_Control(pDX, IDC_SHIP_VARIABLES_COMBO, m_ship_quantity_variable);
DDX_Control(pDX, IDC_WEAPON_VARIABLES_COMBO, m_weapon_quantity_variable);
DDX_Control(pDX, IDC_WINGS_SHP_COUNT, m_ships_used_in_wings);
DDX_Control(pDX, IDC_WINGS_WPN_COUNT, m_weapons_used_in_wings);
DDX_Text(pDX, IDC_DELAY, m_delay);
DDX_Text(pDX, IDC_SHIP_POOL, m_ship_pool);
DDX_Text(pDX, IDC_WEAPON_POOL, m_weapon_pool);
DDX_Check(pDX, IDC_PAD_TOGGLE, m_validation_toggle);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(player_start_editor, CDialog)
//{{AFX_MSG_MAP(player_start_editor)
ON_WM_INITMENU()
ON_LBN_SELCHANGE(IDC_SHIP_LIST, OnSelchangeShipList)
ON_CLBN_CHKCHANGE(IDC_SHIP_LIST, OnSelchangeShipList)
ON_LBN_SELCHANGE(IDC_WEAPON_LIST, OnSelchangeWeaponList)
ON_CLBN_CHKCHANGE(IDC_SHIP_LIST, OnSelchangeWeaponList)
ON_EN_UPDATE(IDC_SHIP_POOL, OnUpdateShipPool)
ON_EN_UPDATE(IDC_WEAPON_POOL, OnUpdateWeaponPool)
ON_LBN_SELCHANGE(IDC_SHIP_VARIABLES_LIST, OnSelchangeShipVariablesList)
ON_LBN_SELCHANGE(IDC_WEAPON_VARIABLES_LIST, OnSelchangeWeaponVariablesList)
ON_CBN_SELCHANGE(IDC_SHIP_VARIABLES_COMBO, OnSelchangeShipVariablesCombo)
ON_WM_CLOSE()
ON_CBN_SELCHANGE(IDC_WEAPON_VARIABLES_COMBO, OnSelchangeWeaponVariablesCombo)
ON_BN_CLICKED(IDC_REQUIRED_WEAPONS, OnRequiredWeapons)
ON_BN_CLICKED(IDC_PAD_TOGGLE, OnSelValidationToggle)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// player_start_editor message handlers
BOOL player_start_editor::OnInitDialog()
{
int i;
// initialize ship pool data
for(i=0; i<MAX_TVT_TEAMS; i++){
static_ship_pool[i].clear();
dynamic_ship_pool[i].clear();
static_ship_variable_pool[i].clear();
dynamic_ship_variable_pool[i].clear();
for (auto &entry : Team_data[i].ship_choices)
{
// do we have a variable for this entry? if we don't....
if (entry.class_variable.empty())
{
// This pool holds the number of ships available, keyed by Ship_info index.
static_ship_pool[i][entry.class_index] = entry.count;
// This pool notes whether the count has been set by a variable (and if so, the
// variable's index in Sexp_variables); no entry means a literal count.
if (!entry.count_variable.empty()) {
static_ship_variable_pool[i][entry.class_index] = get_index_sexp_variable_name(entry.count_variable.c_str());
}
}
// if we do....
else
{
// This pool holds the number of ships available, keyed by Sexp_variables index.
int class_var_index = get_index_sexp_variable_name(entry.class_variable.c_str());
dynamic_ship_pool[i][class_var_index] = entry.count;
if (!entry.count_variable.empty()) {
dynamic_ship_variable_pool[i][class_var_index] = get_index_sexp_variable_name(entry.count_variable.c_str());
}
}
}
}
// initialize weapon pool data
for(i=0; i<MAX_TVT_TEAMS; i++){
static_weapon_pool[i].clear();
dynamic_weapon_pool[i].clear();
static_weapon_variable_pool[i].clear();
dynamic_weapon_variable_pool[i].clear();
for (auto &entry : Team_data[i].weapon_choices)
{
// do we have a variable for this entry?
if (entry.class_variable.empty())
{
static_weapon_pool[i][entry.class_index] = entry.count;
if (!entry.count_variable.empty()) {
static_weapon_variable_pool[i][entry.class_index] = get_index_sexp_variable_name(entry.count_variable.c_str());
}
}
// if we do....
else
{
int class_var_index = get_index_sexp_variable_name(entry.class_variable.c_str());
dynamic_weapon_pool[i][class_var_index] = entry.count;
if (!entry.count_variable.empty()) {
dynamic_weapon_variable_pool[i][class_var_index] = get_index_sexp_variable_name(entry.count_variable.c_str());
}
}
}
}
for (i = 0; i < MAX_TVT_TEAMS; i++) {
validation_toggle[i] = Team_data[i].do_not_validate;
}
// initialise the ship and weapon usage list; the usage generators only fill the teams
// that this mission actually has, so clear the rest here
for (i=0; i<MAX_TVT_TEAMS; i++) {
ship_usage[i].clear();
weapon_usage[i].clear();
}
for (i=0; i<Num_teams; i++) {
generate_ship_usage_list_team(i, ship_usage[i]);
generate_weaponry_usage_list_team(i, weapon_usage[i]);
}
// initialize weapon required flags
for (i = 0; i < MAX_TVT_TEAMS; i++) {
weapon_is_required[i] = Team_data[i].required_weapons;
}
// entry delay time
m_delay = f2i(Entry_delay_time);
// misc window crap
CDialog::OnInitDialog();
theApp.init_window(&Player_wnd_data, this);
m_spin1.SetRange(0, 99);
m_pool_spin.SetRange(0, 9999);
m_delay_spin.SetRange(0, 30);
// fix overlapping checkboxes issue
// https://stackoverflow.com/questions/57951333/cchecklistbox-items-get-overlapped-on-selection-if-app-build-using-visual-studi
m_ship_list.SetFont(GetFont());
m_weapon_list.SetFont(GetFont());
m_ship_variable_list.SetFont(GetFont());
m_weapon_variable_list.SetFont(GetFont());
// regenerate all the controls
reset_controls();
dlg_inited = 1;
return TRUE;
}
// regenerate all controls
void player_start_editor::reset_controls()
{
int i;
int ct;
char buff[TOKEN_LENGTH + TOKEN_LENGTH + 2]; // VariableName[VariableValue]
if (autobalance && (previous_team != -1) && (previous_team != selected_team)) {
// copy across all the ship stuff
static_ship_pool[selected_team] = static_ship_pool[previous_team];
static_ship_variable_pool[selected_team] = static_ship_variable_pool[previous_team];
// copy across weapon stuff
static_weapon_pool[selected_team] = static_weapon_pool[previous_team];
static_weapon_variable_pool[selected_team] = static_weapon_variable_pool[previous_team];
// copy across variable stuff
dynamic_ship_pool[selected_team] = dynamic_ship_pool[previous_team];
dynamic_ship_variable_pool[selected_team] = dynamic_ship_variable_pool[previous_team];
dynamic_weapon_pool[selected_team] = dynamic_weapon_pool[previous_team];
dynamic_weapon_variable_pool[selected_team] = dynamic_weapon_variable_pool[previous_team];
}
m_ship_variable_list.ResetContent();
m_ship_quantity_variable.ResetContent();
m_weapon_variable_list.ResetContent();
m_weapon_quantity_variable.ResetContent();
// Add the default entry to both variable quantity ComboBoxes
m_ship_quantity_variable.AddString("Don't Use Variables");
m_weapon_quantity_variable.AddString("Don't Use Variables");
int current_entry = 0;
int num_sexp_variables = sexp_variable_count();
for (i=0; i < num_sexp_variables; i++)
{
if (Sexp_variables[i].type & SEXP_VARIABLE_STRING)
{
sprintf(buff, "%s [%s]", Sexp_variables[i].variable_name, Sexp_variables[i].text);
m_ship_variable_list.AddString(buff);
m_weapon_variable_list.AddString(buff);
// Now we set the checkbox for ships
if((dynamic_ship_pool[selected_team].value_or(i, -1) > 0) ||
(dynamic_ship_variable_pool[selected_team].contains(i)))
{
m_ship_variable_list.SetCheck(current_entry, TRUE);
}
else
{
m_ship_variable_list.SetCheck(current_entry, FALSE);
}
// and now for weapons
if((dynamic_weapon_pool[selected_team].value_or(i, -1) > 0) ||
(dynamic_weapon_variable_pool[selected_team].contains(i)))
{
m_weapon_variable_list.SetCheck(current_entry, TRUE);
}
else
{
m_weapon_variable_list.SetCheck(current_entry, FALSE);
}
current_entry++;
}
// Since we're looping through Sexp_variables amyway we might as well fill the number variable combo boxes
else if (Sexp_variables[i].type & SEXP_VARIABLE_NUMBER)
{
m_ship_quantity_variable.AddString(Sexp_variables[i].variable_name);
m_weapon_quantity_variable.AddString(Sexp_variables[i].variable_name);
}
}
// create a checklistbox for each "player" ship type
m_ship_list.ResetContent();
ct = 0;
for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it) {
if (it->flags[Ship::Info_Flags::Player_ship]) {
i = (int)std::distance(Ship_info.cbegin(), it);
m_ship_list.AddString(it->name);
// if the ship currently has pool entries or was set by a variable, check it
if ((static_ship_pool[selected_team].value_or(i, -1) > 0) || (static_ship_variable_pool[selected_team].contains(i))) {
m_ship_list.SetCheck(ct, TRUE);
}
else {
m_ship_list.SetCheck(ct, FALSE);
}
// next
ct++;
}
}
// create a checklistbox for each weapon ship type
m_weapon_list.ResetContent();
ct = 0;
for (i=0; i<weapon_info_size(); i++) {
if (Weapon_info[i].wi_flags[Weapon::Info_Flags::Player_allowed]) {
m_weapon_list.AddString(Weapon_info[i].name);
// if the ship currently has pool entries or was set by a variable, check it
if((static_weapon_pool[selected_team].value_or(i, -1) > 0) || (static_weapon_variable_pool[selected_team].contains(i))){
m_weapon_list.SetCheck(ct, TRUE);
} else {
m_weapon_list.SetCheck(ct, FALSE);
}
ct++;
} else if (static_weapon_pool[selected_team].value_or(i, -1) > 0 || (static_weapon_variable_pool[selected_team].contains(i))) {
// not sure if this should be a verbal warning or not, so I'm adding both and making it verbal for now
Warning(LOCATION, "Weapon '%s' in weapon pool isn't allowed on player loadout! Resetting count to 0...\n", Weapon_info[i].name);
static_weapon_pool[selected_team].erase(i);
static_weapon_variable_pool[selected_team].erase(i);
}
}
m_validation_toggle = validation_toggle[selected_team];
// be sure that nothing is selected
m_ship_list.SetCurSel(-1);
m_ship_variable_list.SetCurSel(-1);
m_ship_quantity_variable.SetCurSel(-1);
m_weapon_list.SetCurSel(-1);
m_weapon_variable_list.SetCurSel(-1);
m_weapon_quantity_variable.SetCurSel(-1);
UpdateData(FALSE);
}
void player_start_editor::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(selected_team, MF_BYPOSITION | MF_CHECKED);
m = pMenu->GetSubMenu(1);
if (Num_teams > 1) {
m->CheckMenuItem(ID_AUTOBALANCE, autobalance ? MF_CHECKED : MF_UNCHECKED);
}
else {
m->EnableMenuItem(ID_AUTOBALANCE, MF_GRAYED);
}
CDialog::OnInitMenu(pMenu);
}
// switch between active teams
BOOL player_start_editor::OnCommand(WPARAM wParam, LPARAM lParam)
{
int id;
// select a team
id = LOWORD(wParam);
switch(id){
case ID_TEAM_1:
UpdateData();
previous_team = selected_team;
selected_team = 0;
reset_controls();
break;
case ID_TEAM_2:
UpdateData();
previous_team = selected_team;
selected_team = 1;
reset_controls();
break;
case ID_AUTOBALANCE:
autobalance = !autobalance;
break;
}
// low level stuff
return CDialog::OnCommand(wParam, lParam);
}
// ship list changed
void player_start_editor::OnSelchangeShipList()
{
// make sure internal variables have the latest inputs
UpdateData();
int selected;
int si_index;
char ship_name[255] = "";
char ship_usage_buff[10];
// If the ship list is selected the variable ship list should be deselected
m_ship_variable_list.SetCurSel(-1);
// determine if we've selected something
selected = m_ship_list.GetCurSel();
if (selected != -1) {
// lookup the ship
m_ship_list.GetText(selected, ship_name);
si_index = ship_info_lookup(ship_name);
// if we have a valid ship type
if(si_index >= 0){
// if this item is checked
if(m_ship_list.GetCheck(selected)) {
if (!static_ship_variable_pool[selected_team].contains(si_index)) {
if (static_ship_pool[selected_team].value_or(si_index, -1) <= 0){
static_ship_pool[selected_team][si_index] = 5;
}
m_ship_pool = static_ship_pool[selected_team][si_index];
// Set the ship variable ComboBox to reflect that we are not using variables for this ship
m_ship_quantity_variable.SetCurSel(0);
}
// If the number of ships was set by a variable
else {
int count_var_index = static_ship_variable_pool[selected_team][si_index];
Assert (Sexp_variables[count_var_index].type & SEXP_VARIABLE_NUMBER);
m_ship_pool = atoi(Sexp_variables[count_var_index].text);
int selected_variable = sexp_variable_typed_count(count_var_index, SEXP_VARIABLE_NUMBER);
m_ship_quantity_variable.SetCurSel(selected_variable + 1);
}
}
// otherwise zero the count
else {
static_ship_pool[selected_team].erase(si_index);
static_ship_variable_pool[selected_team].erase(si_index);
m_ship_pool = 0;
m_ship_quantity_variable.SetCurSel(0);
}
// set the number used in wings
sprintf(ship_usage_buff, "%d", ship_usage[selected_team].value_or(si_index, 0));
m_ships_used_in_wings.SetWindowText(ship_usage_buff);
} else {
Int3();
}
}
// update stuff
UpdateData(FALSE);
}
// ship variable list changed
void player_start_editor::OnSelchangeShipVariablesList()
{
// make sure internal variables have the latest inputs
UpdateData();
int selection;
// If the variable list is selected the ship list should be deselected
m_ship_list.SetCurSel(-1);
//Have we selected something?
selection = m_ship_variable_list.GetCurSel();
if (selection != -1) {
int sexp_index = get_nth_variable_index(selection+1, SEXP_VARIABLE_STRING);
if (sexp_index > -1) {
Assert(selection == sexp_variable_typed_count(sexp_index, SEXP_VARIABLE_STRING));
// Is this item checked?
if (m_ship_variable_list.GetCheck(selection)) {
if (!dynamic_ship_variable_pool[selected_team].contains(sexp_index)) {
if (dynamic_ship_pool[selected_team].value_or(sexp_index, -1) <= 0) {
dynamic_ship_pool[selected_team][sexp_index] = 5;
}
m_ship_pool = dynamic_ship_pool[selected_team][sexp_index];
m_ship_quantity_variable.SetCurSel(0);
}
else {
int count_var_index = dynamic_ship_variable_pool[selected_team][sexp_index];
Assert (Sexp_variables[count_var_index].type & SEXP_VARIABLE_NUMBER);
m_ship_pool = atoi(Sexp_variables[count_var_index].text);
int selected_variable = sexp_variable_typed_count(count_var_index, SEXP_VARIABLE_NUMBER);
m_ship_quantity_variable.SetCurSel(selected_variable + 1);
}
}
// We've unselected the tickbox, reset everything
else {
dynamic_ship_pool[selected_team].erase(sexp_index);
dynamic_ship_variable_pool[selected_team].erase(sexp_index);
m_ship_pool = 0;
m_ship_quantity_variable.SetCurSel(0);
}
// It might be nice to have FRED work out if any ships of the class represented by the variable are in the wings
// but for now just set it to zero
m_ships_used_in_wings.SetWindowText("0");
}
else {
Int3();
}
}
// update stuff
UpdateData(FALSE);
}
void player_start_editor::OnSelchangeShipVariablesCombo()
{
// make sure internal variables have the latest inputs
UpdateData();
// Get the new selection
char variable_name[TOKEN_LENGTH];
bool update_static_pool = false;
bool update_dynamic_pool = false;
m_ship_quantity_variable.GetLBText(m_ship_quantity_variable.GetCurSel(), variable_name);
int sexp_index = get_index_sexp_variable_name(variable_name);
Assert ((sexp_index > -1) || (!strcmp("Don't Use Variables", variable_name)));
// See if the ship_list was selected
int ship_index = GetSelectedShipListIndex();
if (ship_index >= 0) {
if (sexp_index >= 0)
static_ship_variable_pool[selected_team][ship_index] = sexp_index;
else
static_ship_variable_pool[selected_team].erase(ship_index);
update_static_pool = true;
}
// Maybe it's the ship_variables_list that is actually selected
int ship_variable_index = GetSelectedShipVariableListIndex();
if (ship_variable_index >= 0 ) {
if (sexp_index >= 0)
dynamic_ship_variable_pool[selected_team][ship_variable_index] = sexp_index;
else
dynamic_ship_variable_pool[selected_team].erase(ship_variable_index);
update_dynamic_pool = true;
}
// Somethings gone wrong if they're both marked as true
Assert (! (update_static_pool && update_dynamic_pool));
// Update the ship_pool
if (update_static_pool || update_dynamic_pool) {
int new_quantity = 5;
if (sexp_index > -1) {
Assert (Sexp_variables[sexp_index].type & SEXP_VARIABLE_NUMBER);
new_quantity = atoi(Sexp_variables[sexp_index].text);
}
if (update_static_pool) {
static_ship_pool[selected_team][ship_index] = new_quantity;
}
else {
dynamic_ship_pool[selected_team][ship_variable_index] = new_quantity;
}
m_ship_pool = new_quantity;
}
// update stuff
UpdateData(FALSE);
}
// weapon list changed
void player_start_editor::OnSelchangeWeaponList()
{
// make sure internal variables have the latest inputs
UpdateData();
int selected;
int wi_index;
char weapon_name[255] = "";
char weapon_usage_buff[10];
// If the weapon list is selected the variable weapon list shouldn't be
m_weapon_variable_list.SetCurSel(-1);
// determine if we've selected something
selected = m_weapon_list.GetCurSel();
if (selected != -1) {
// lookup the weapon
m_weapon_list.GetText(selected, weapon_name);
wi_index = weapon_info_lookup(weapon_name);
// if we have a valid ship type
if(wi_index >= 0){
// if this item is checked
if(m_weapon_list.GetCheck(selected)) {
if (!static_weapon_variable_pool[selected_team].contains(wi_index)) {
if (static_weapon_pool[selected_team].value_or(wi_index, -1) <= 0){
static_weapon_pool[selected_team][wi_index] = 100;
}
m_weapon_pool = static_weapon_pool[selected_team][wi_index];
// Set the combo reflect that we are not using variables for this weapon
m_weapon_quantity_variable.SetCurSel(0);
}
// If the number of ships was set by a variable
else {
int count_var_index = static_weapon_variable_pool[selected_team][wi_index];
Assert (Sexp_variables[count_var_index].type & SEXP_VARIABLE_NUMBER);
m_weapon_pool = atoi(Sexp_variables[count_var_index].text);
int selected_variable = sexp_variable_typed_count(count_var_index, SEXP_VARIABLE_NUMBER);
m_weapon_quantity_variable.SetCurSel(selected_variable + 1);
}
}
// otherwise zero the count
else {
static_weapon_pool[selected_team].erase(wi_index);
static_weapon_variable_pool[selected_team].erase(wi_index);
m_weapon_pool = 0;
m_weapon_quantity_variable.SetCurSel(0);
}
// set the number used in wings
sprintf(weapon_usage_buff, "%d", weapon_usage[selected_team].value_or(wi_index, 0));
m_weapons_used_in_wings.SetWindowText(weapon_usage_buff);
} else {
Int3();
}
}
// update stuff
UpdateData(FALSE);
}
void player_start_editor::OnSelchangeWeaponVariablesList()
{
// make sure internal variables have the latest inputs
UpdateData();
int selection;
// deselect the other list
m_weapon_list.SetCurSel(-1);
//Have we selected something?
selection = m_weapon_variable_list.GetCurSel();
if (selection != -1) {
int sexp_index = get_nth_variable_index(selection+1, SEXP_VARIABLE_STRING);
if (sexp_index > -1) {
Assert(selection == sexp_variable_typed_count(sexp_index, SEXP_VARIABLE_STRING));
// Is this item checked?
if (m_weapon_variable_list.GetCheck(selection)) {
if (!dynamic_weapon_variable_pool[selected_team].contains(sexp_index)) {
if (dynamic_weapon_pool[selected_team].value_or(sexp_index, -1) <= 0) {
dynamic_weapon_pool[selected_team][sexp_index] = 5;
}
m_weapon_pool = dynamic_weapon_pool[selected_team][sexp_index];
m_weapon_quantity_variable.SetCurSel(0);
}
else {
int count_var_index = dynamic_weapon_variable_pool[selected_team][sexp_index];
Assert (Sexp_variables[count_var_index].type & SEXP_VARIABLE_NUMBER);
m_weapon_pool = atoi(Sexp_variables[count_var_index].text);
int selected_variable = sexp_variable_typed_count(count_var_index, SEXP_VARIABLE_NUMBER);
m_weapon_quantity_variable.SetCurSel(selected_variable + 1);
}
}
// We've unselected the tickbox, reset everything
else {
dynamic_weapon_pool[selected_team].erase(sexp_index);
dynamic_weapon_variable_pool[selected_team].erase(sexp_index);
m_weapon_pool = 0;
m_weapon_quantity_variable.SetCurSel(0);
}
// It might be nice to have FRED work out if any weapons of this type are in the wings
// but for now just set it to zero
m_weapons_used_in_wings.SetWindowText("0");
}
else {
Int3();
}
}
// update stuff
UpdateData(FALSE);
}
void player_start_editor::OnSelchangeWeaponVariablesCombo()
{
// make sure internal variables have the latest inputs
UpdateData();
// Get the new selection
char variable_name[TOKEN_LENGTH];
bool update_static_pool = false;
bool update_dynamic_pool = false;
m_weapon_quantity_variable.GetLBText(m_weapon_quantity_variable.GetCurSel(), variable_name);
int sexp_index = get_index_sexp_variable_name(variable_name);
Assert ((sexp_index > -1) || (!strcmp("Don't Use Variables", variable_name)));
// See if the weapon_list was selected
int weapon_index = GetSelectedWeaponListIndex();
if (weapon_index >= 0) {
if (sexp_index >= 0)
static_weapon_variable_pool[selected_team][weapon_index] = sexp_index;
else
static_weapon_variable_pool[selected_team].erase(weapon_index);
update_static_pool = true;
}
// Maybe it's the weapon_variables_list that is actually selected
int weapon_variable_index = GetSelectedWeaponVariableListIndex();
if (weapon_variable_index >= 0 ) {
if (sexp_index >= 0)
dynamic_weapon_variable_pool[selected_team][weapon_variable_index] = sexp_index;
else
dynamic_weapon_variable_pool[selected_team].erase(weapon_variable_index);
update_dynamic_pool = true;
}
// Somethings gone wrong if they're both marked as true
Assert (! (update_static_pool && update_dynamic_pool));
// Update the weapon_pool
if (update_static_pool || update_dynamic_pool) {
int new_quantity = 100;
if (sexp_index > -1) {
Assert (Sexp_variables[sexp_index].type & SEXP_VARIABLE_NUMBER);
new_quantity = atoi(Sexp_variables[sexp_index].text);
}
if (update_static_pool) {
static_weapon_pool[selected_team][weapon_index] = new_quantity;
}
else {
dynamic_weapon_pool[selected_team][weapon_variable_index] = new_quantity;
}
m_weapon_pool = new_quantity;
}
// update stuff
UpdateData(FALSE);
}
void player_start_editor::OnRequiredWeapons()
{
// create a list of options with just the weapons that are in the static pool
SCP_vector<int> weapon_indexes;
SCP_vector<std::pair<CString, bool>> options;
for (const auto &[weapon_class, count] : static_weapon_pool[selected_team])
{
if (count > 0)
{
weapon_indexes.push_back(weapon_class);
options.emplace_back(Weapon_info[weapon_class].name, weapon_is_required[selected_team].contains(weapon_class));
}
}
// display the checklist
CheckBoxListDlg dlg;
dlg.SetCaption("Required Weapons");
dlg.SetOptions(options);
dlg.DoModal();
// reassign required weapons
for (int i = 0; i < sz2i(options.size()); i++) {
if (dlg.IsChecked(i))
weapon_is_required[selected_team].insert(weapon_indexes[i]);
else
weapon_is_required[selected_team].erase(weapon_indexes[i]);
}
}
// cancel
void player_start_editor::OnCancel()
{
theApp.record_window_data(&Player_wnd_data, this);
CDialog::OnCancel();
}
// ok
void player_start_editor::OnOK()
{
int i, idx;
UpdateData();
int num_sexp_variables = sexp_variable_count();
// store player entry time delay
Entry_delay_time = i2f(m_delay);
// store ship pools
for(i=0; i<MAX_TVT_TEAMS; i++) {
Team_data[i].ship_choices.clear();
// First look through the variables list and write out anything there
for (idx=0; idx < num_sexp_variables; idx++) {
// As soon as we come across a sexp_variable we are using
if (dynamic_ship_pool[i].contains(idx)) {
Assert (Sexp_variables[idx].type & SEXP_VARIABLE_STRING);
int ship_class = ship_info_lookup(Sexp_variables[idx].text);
// If the variable doesn't actually contain a valid ship class name. Warn the user and skip to the next one
if (ship_class < 0) {
char buffer[256];
sprintf(buffer,
"Sexp Variable %s holds the value %s. This is not a valid ship class. Skipping this entry",
Sexp_variables[idx].variable_name,
Sexp_variables[idx].text
);
MessageBox(buffer);
continue;
}
// Copy the variable to Team_data
auto &entry = Team_data[i].ship_choices.emplace_back();
entry.class_variable = Sexp_variables[idx].variable_name;
entry.class_index = -1;
// Now we need to set the number of this type available
auto count_var = dynamic_ship_variable_pool[i].find(idx);
if (count_var == dynamic_ship_variable_pool[i].end()) {
entry.count = dynamic_ship_pool[i][idx];
}
else {
Assert (Sexp_variables[count_var->second].type & SEXP_VARIABLE_NUMBER);
entry.count_variable = Sexp_variables[count_var->second].variable_name;
entry.count = atoi(Sexp_variables[count_var->second].text);
}
}
}
// Now we deal with the loadout ships that are statically assigned by class
for (idx = 0; idx < ship_info_size(); idx++) {
// if we have ships here
if(static_ship_pool[i].value_or(idx, -1) > 0 || static_ship_variable_pool[i].contains(idx)) {
auto &entry = Team_data[i].ship_choices.emplace_back();
entry.class_index = idx;
// Now set the number of this class available
auto count_var = static_ship_variable_pool[i].find(idx);
if (count_var == static_ship_variable_pool[i].end()) {
entry.count = static_ship_pool[i][idx];
}
else {
Assert (Sexp_variables[count_var->second].type & SEXP_VARIABLE_NUMBER);
entry.count_variable = Sexp_variables[count_var->second].variable_name;
entry.count = atoi(Sexp_variables[count_var->second].text);
}
}
}
}
// store weapon pools
for(i=0; i<MAX_TVT_TEAMS; i++){
Team_data[i].weapon_choices.clear();
// First look through the variables list and write out anything there
for (idx=0; idx < num_sexp_variables; idx++) {
// As soon as we come across a sexp_variable we are using
if (dynamic_weapon_pool[i].contains(idx)) {
Assert (Sexp_variables[idx].type & SEXP_VARIABLE_STRING);
int weapon_class = weapon_info_lookup(Sexp_variables[idx].text);
// If the variable doesn't actually contain a valid ship class name. Warn the user and skip to the next one
if (weapon_class < 0)
{
char buffer[256];
sprintf(buffer,
"Sexp Variable %s holds the value %s. This is not a valid weapon class. Skipping this entry",
Sexp_variables[idx].variable_name,
Sexp_variables[idx].text
);
MessageBox(buffer);
continue;
}
// Copy the variable to Team_data
auto &entry = Team_data[i].weapon_choices.emplace_back();
entry.class_variable = Sexp_variables[idx].variable_name;
entry.class_index = -1;
// Now we need to set the number of this class available
auto count_var = dynamic_weapon_variable_pool[i].find(idx);
if (count_var == dynamic_weapon_variable_pool[i].end())
{
entry.count = dynamic_weapon_pool[i][idx];
}
else
{
Assert (Sexp_variables[count_var->second].type & SEXP_VARIABLE_NUMBER);
entry.count_variable = Sexp_variables[count_var->second].variable_name;
entry.count = atoi(Sexp_variables[count_var->second].text);
}
}
}
// Now we deal with the loadout weapons that are statically assigned by class
for(idx=0; idx<weapon_info_size(); idx++)
{
// if we have weapons here
if(static_weapon_pool[i].value_or(idx, -1) > 0 || static_weapon_variable_pool[i].contains(idx))
{
auto &entry = Team_data[i].weapon_choices.emplace_back();
entry.class_index = idx;
// Now set the number of this class available
auto count_var = static_weapon_variable_pool[i].find(idx);
if (count_var == static_weapon_variable_pool[i].end())
{
entry.count = static_weapon_pool[i][idx];
}
else
{
Assert (Sexp_variables[count_var->second].type & SEXP_VARIABLE_NUMBER);
entry.count_variable = Sexp_variables[count_var->second].variable_name;
entry.count = atoi(Sexp_variables[count_var->second].text);
}
}
}
}
// store the loadout padding toggle
for (i = 0; i < MAX_TVT_TEAMS; i++) {
Team_data[i].do_not_validate = validation_toggle[i];
}
// store required weapons
for (i = 0; i < MAX_TVT_TEAMS; i++) {
Team_data[i].required_weapons.clear();
for (int weapon_class : weapon_is_required[i]) {
if (static_weapon_pool[i].value_or(weapon_class, -1) > 0) {
Team_data[i].required_weapons.insert(weapon_class);
} else {
SCP_string buffer = "Cannot require a weapon (";
buffer += Weapon_info[weapon_class].name;
buffer += ") that is not in the static weaponry pool! This weapon will be skipped.";
MessageBox(buffer.c_str());
}
}
}
theApp.record_window_data(&Player_wnd_data, this);
CDialog::OnOK();
FREDDoc_ptr->autosave("loadout editor");
}
// Returns the ship_info index of the selected and checked ship_list item or -1 if nothing is checked or
// the ship is invalid
int player_start_editor::GetSelectedShipListIndex()
{
char name[255] = "";
int selected = m_ship_list.GetCurSel();
if((selected != -1) && m_ship_list.GetCheck(selected))
{
// lookup the ship
m_ship_list.GetText(m_ship_list.GetCurSel(), name);
int ship_index = ship_info_lookup(name);
return ship_index;
}
return -1;
}
// Returns the weapon_info index of the selected and checked ship_list item or -1 if nothing is checked or
// the weapon is invalid
int player_start_editor::GetSelectedWeaponListIndex()
{
char name[255] = "";
int selected = m_weapon_list.GetCurSel();
if((selected != -1) && m_weapon_list.GetCheck(selected))
{
// lookup the weapon
m_weapon_list.GetText(m_weapon_list.GetCurSel(), name);
int weapon_index = weapon_info_lookup(name);
return weapon_index;
}
return -1;
}
// Returns the Sexp_variables index of the selected and checked ship_variables_list item or -1 if nothing is checked
// or the ship is invalid
int player_start_editor::GetSelectedShipVariableListIndex()
{
// Try the ship_variables_list
int selected = m_ship_variable_list.GetCurSel();
if((selected != -1) && m_ship_variable_list.GetCheck(selected))
{
//lookup the variable
return get_nth_variable_index(selected+1, SEXP_VARIABLE_STRING);
}
return -1;
}
// Returns the Sexp_variables index of the selected and checked weapon_variables_list item or -1 if nothing is checked
// or the weapon is invalid
int player_start_editor::GetSelectedWeaponVariableListIndex()
{
// Try the ship_variables_list
int selected = m_weapon_variable_list.GetCurSel();
if((selected != -1) && m_weapon_variable_list.GetCheck(selected))
{
return get_nth_variable_index(selected+1, SEXP_VARIABLE_STRING);
}
return -1;
}
// Updates the Sexp_variable in a variable combobox to the value supplied
void player_start_editor::UpdateQuantityVariable(CComboBox *variable_list, int pool_value)
{
char variable_name[TOKEN_LENGTH];
variable_list->GetLBText(variable_list->GetCurSel(), variable_name);
int variable_index = get_index_sexp_variable_name(variable_name);
if (variable_index > -1)
{
char variable_value[TOKEN_LENGTH];
sprintf(variable_value, "%d", pool_value);
Assert (Sexp_variables[variable_index].type & SEXP_VARIABLE_NUMBER);
sexp_fred_modify_variable(variable_value, variable_name, variable_index, SEXP_VARIABLE_NUMBER);
}
}
// ship pool count change
void player_start_editor::OnUpdateShipPool()
{
int si_index;
if (!dlg_inited){
return;
}
UpdateData(TRUE);
si_index = GetSelectedShipListIndex();
// Update the pool if we have a valid ship type
if(si_index >= 0)
{
static_ship_pool[selected_team][si_index] = m_ship_pool;
// If this value came from a variable we should update its value
UpdateQuantityVariable(&m_ship_quantity_variable, m_ship_pool);
return ;
}
// Maybe it's the ship_variables_list that is actually selected
int sexp_index = GetSelectedShipVariableListIndex();
// if we have a valid sexp_variable
if(sexp_index >= 0)
{
dynamic_ship_pool[selected_team][sexp_index] = m_ship_pool;
UpdateQuantityVariable(&m_ship_quantity_variable, m_ship_pool);
}
}
// weapon pool count change
void player_start_editor::OnUpdateWeaponPool()
{
int wi_index;
if (!dlg_inited){
return;
}
UpdateData(TRUE);
wi_index = GetSelectedWeaponListIndex();
// Update the pool if we have a valid weapon type
if(wi_index >= 0)
{
static_weapon_pool[selected_team][wi_index] = m_weapon_pool;
// If this value came from a variable we should update its value
UpdateQuantityVariable(&m_weapon_quantity_variable, m_weapon_pool);
return ;
}
// Maybe it's the ship_variables_list that is actually selected
int sexp_index = GetSelectedWeaponVariableListIndex();
// if we have a valid sexp_variable
if(sexp_index >= 0)
{
dynamic_weapon_pool[selected_team][sexp_index] = m_weapon_pool;
UpdateQuantityVariable(&m_weapon_quantity_variable, m_weapon_pool);
}
}
void player_start_editor::OnSelValidationToggle()
{
validation_toggle[selected_team] = !m_validation_toggle;
UpdateData(TRUE);
}