mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
The team color combo box is only populated and enabled in single-edit mode for a ship class that uses team colors. In multi-edit (or for a class without team colors) the combo stays disabled, and OnOK() fell into the unconditional else branch that reset the current ship's team_name to none. Only apply the combo selection when the combo is actually enabled, matching the qtFRED fix in #7403. Co-Authored-By: Claude Fable 5 <[email protected]>
1155 lines
32 KiB
C++
1155 lines
32 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.
|
|
*
|
|
*/
|
|
|
|
// InitialStatus.cpp : implementation file
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "FRED.h"
|
|
#include "FREDDoc.h"
|
|
#include "FREDView.h"
|
|
#include "InitialStatus.h"
|
|
#include "Management.h"
|
|
#include "globalincs/linklist.h"
|
|
#include "globalincs/alphacolors.h"
|
|
#include "object/objectdock.h"
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
|
|
// non-class function prototypes, bah
|
|
void initial_status_mark_dock_leader_helper(object *objp, dock_function_info *infop);
|
|
void initial_status_unmark_dock_handled_flag(object *objp, dock_function_info *infop);
|
|
void reset_arrival_to_false(int shipnum, bool reset_wing);
|
|
|
|
/**
|
|
* @brief Handles setting a flag on a flagset when the value is inconsistent
|
|
*
|
|
* This is necessary in case multiple ships with inconsistent object flags have been selected in which case
|
|
* that flag may not be edited since it would corrupt the value of that flag. This function simplifies handling
|
|
* that case.
|
|
*/
|
|
template<typename T>
|
|
static void handle_inconsistent_flag(flagset<T>& flags, T flag, int value) {
|
|
if (value == 1) {
|
|
flags.set(flag);
|
|
} else if (value == 0) {
|
|
flags.remove(flag);
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// initial_status dialog
|
|
|
|
initial_status::initial_status(CWnd* pParent /*=NULL*/)
|
|
: CDialog(initial_status::IDD, pParent)
|
|
{
|
|
//{{AFX_DATA_INIT(initial_status)
|
|
m_damage = 0;
|
|
m_shields = 0;
|
|
m_velocity = 0;
|
|
m_hull = 0;
|
|
m_has_shields = FALSE;
|
|
m_force_shields = FALSE;
|
|
m_ship_locked = FALSE;
|
|
m_weapons_locked = FALSE;
|
|
m_primaries_locked = FALSE;
|
|
m_secondaries_locked = FALSE;
|
|
m_turrets_locked = FALSE;
|
|
m_afterburner_locked = FALSE;
|
|
m_cargo_name = _T("");
|
|
//}}AFX_DATA_INIT
|
|
inited = 0;
|
|
cur_subsys = LB_ERR;
|
|
|
|
dockpoint_array = NULL;
|
|
num_dock_points = 0;
|
|
|
|
cur_docker_point = -1;
|
|
cur_dockee = -1;
|
|
cur_dockee_point = -1;
|
|
|
|
m_multi_edit = 0;
|
|
}
|
|
|
|
initial_status::~initial_status()
|
|
{
|
|
if (dockpoint_array != NULL)
|
|
delete [] dockpoint_array;
|
|
}
|
|
|
|
void initial_status::DoDataExchange(CDataExchange* pDX)
|
|
{
|
|
CString str;
|
|
|
|
CDialog::DoDataExchange(pDX);
|
|
//{{AFX_DATA_MAP(initial_status)
|
|
DDX_Control(pDX, IDC_HULL_SPIN, m_hull_spin);
|
|
DDX_Control(pDX, IDC_VELOCITY_SPIN, m_velocity_spin);
|
|
DDX_Control(pDX, IDC_SHIELDS_SPIN, m_shields_spin);
|
|
DDX_Control(pDX, IDC_DAMAGE_SPIN, m_damage_spin);
|
|
DDX_Control(pDX, IDC_TEAMSELECT, m_team_color_setting);
|
|
DDX_Text(pDX, IDC_DAMAGE, m_damage);
|
|
DDV_MinMaxInt(pDX, m_damage, 0, 100);
|
|
DDX_Check(pDX, IDC_HAS_SHIELDS, m_has_shields);
|
|
DDX_Check(pDX, IDC_FORCE_SHIELDS, m_force_shields);
|
|
DDX_Check(pDX, IDC_SHIP_LOCKED, m_ship_locked);
|
|
DDX_Check(pDX, IDC_WEAPONS_LOCKED, m_weapons_locked);
|
|
DDX_Check(pDX, IDC_PRIMARIES_LOCKED, m_primaries_locked);
|
|
DDX_Check(pDX, IDC_SECONDARIES_LOCKED, m_secondaries_locked);
|
|
DDX_Check(pDX, IDC_TURRETS_LOCKED, m_turrets_locked);
|
|
DDX_Check(pDX, IDC_AFTERBURNER_LOCKED, m_afterburner_locked);
|
|
DDX_Text(pDX, IDC_CARGO_NAME, m_cargo_name);
|
|
DDV_MaxChars(pDX, m_cargo_name, 20);
|
|
//}}AFX_DATA_MAP
|
|
|
|
if (pDX->m_bSaveAndValidate) {
|
|
GetDlgItem(IDC_VELOCITY)->GetWindowText(str);
|
|
m_velocity = atoi(str);
|
|
if (m_velocity < 0)
|
|
m_velocity = 0;
|
|
if (m_velocity > 100)
|
|
m_velocity = 100;
|
|
|
|
GetDlgItem(IDC_SHIELDS)->GetWindowText(str);
|
|
m_shields = atoi(str);
|
|
if (m_shields < 0)
|
|
m_shields = 0;
|
|
if (m_shields > 100)
|
|
m_shields = 100;
|
|
|
|
GetDlgItem(IDC_HULL)->GetWindowText(str);
|
|
m_hull = atoi(str);
|
|
if (m_hull < 0)
|
|
m_hull = 0;
|
|
if (m_hull > 100)
|
|
m_hull = 100;
|
|
}
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(initial_status, CDialog)
|
|
//{{AFX_MSG_MAP(initial_status)
|
|
ON_LBN_SELCHANGE(IDC_SUBSYS, OnSelchangeSubsys)
|
|
ON_LBN_SELCHANGE(IDC_DOCKER_POINT, OnSelchangeDockerPoint)
|
|
ON_CBN_SELCHANGE(IDC_DOCKEE, OnSelchangeDockee)
|
|
ON_CBN_SELCHANGE(IDC_DOCKEE_POINT, OnSelchangeDockeePoint)
|
|
ON_BN_CLICKED(IDC_HAS_SHIELDS, OnHasShields)
|
|
ON_BN_CLICKED(IDC_FORCE_SHIELDS, OnForceShields)
|
|
ON_BN_CLICKED(IDC_SHIP_LOCKED, OnShipLocked)
|
|
ON_BN_CLICKED(IDC_WEAPONS_LOCKED, OnWeaponsLocked)
|
|
ON_BN_CLICKED(IDC_PRIMARIES_LOCKED, OnPrimariesLocked)
|
|
ON_BN_CLICKED(IDC_SECONDARIES_LOCKED, OnSecondariesLocked)
|
|
ON_BN_CLICKED(IDC_TURRETS_LOCKED, OnTurretsLocked)
|
|
ON_BN_CLICKED(IDC_AFTERBURNER_LOCKED, OnAfterburnersLocked)
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// initial_status message handlers
|
|
|
|
BOOL initial_status::OnInitDialog()
|
|
{
|
|
int z, vflag, sflag, hflag;
|
|
ship_subsys *ptr;
|
|
CString str;
|
|
object *objp = NULL;
|
|
|
|
m_ship = cur_ship;
|
|
if (m_ship == -1) {
|
|
Assert((Objects[cur_object_index].type == OBJ_SHIP) || (Objects[cur_object_index].type == OBJ_START));
|
|
m_ship = get_ship_from_obj(cur_object_index);
|
|
Assert(m_ship >= 0);
|
|
}
|
|
|
|
// initialize dockpoint stuff
|
|
if (!m_multi_edit)
|
|
{
|
|
num_dock_points = model_get_num_dock_points(Ship_info[Ships[m_ship].ship_info_index].model_num);
|
|
dockpoint_array = new dockpoint_information[num_dock_points];
|
|
objp = &Objects[Ships[m_ship].objnum];
|
|
for (int i = 0; i < num_dock_points; i++)
|
|
{
|
|
object *docked_objp = dock_find_object_at_dockpoint(objp, i);
|
|
if (docked_objp != NULL)
|
|
{
|
|
dockpoint_array[i].dockee_shipnum = docked_objp->instance;
|
|
dockpoint_array[i].dockee_point = dock_find_dockpoint_used_by_object(docked_objp, objp);
|
|
}
|
|
else
|
|
{
|
|
dockpoint_array[i].dockee_shipnum = -1;
|
|
dockpoint_array[i].dockee_point = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
vflag = sflag = hflag = 0;
|
|
m_velocity = (int) Objects[cur_object_index].phys_info.speed;
|
|
m_shields = (int) Objects[cur_object_index].shield_quadrant[0];
|
|
m_hull = (int) Objects[cur_object_index].hull_strength;
|
|
if (Objects[cur_object_index].flags[Object::Object_Flags::No_shields])
|
|
m_has_shields = 0;
|
|
else
|
|
m_has_shields = 1;
|
|
|
|
if (Ships[m_ship].flags[Ship::Ship_Flags::Force_shields_on])
|
|
m_force_shields = 1;
|
|
else
|
|
m_force_shields = 0;
|
|
|
|
if (Ships[m_ship].flags[Ship::Ship_Flags::Ship_locked])
|
|
m_ship_locked = 1;
|
|
else
|
|
m_ship_locked = 0;
|
|
|
|
if (Ships[m_ship].flags[Ship::Ship_Flags::Weapons_locked])
|
|
m_weapons_locked = 1;
|
|
else
|
|
m_weapons_locked = 0;
|
|
|
|
// Lock primaries
|
|
if (Ships[m_ship].flags[Ship::Ship_Flags::Primaries_locked]) {
|
|
m_primaries_locked = 1;
|
|
}
|
|
else {
|
|
m_primaries_locked = 0;
|
|
}
|
|
|
|
//Lock secondaries
|
|
if (Ships[m_ship].flags[Ship::Ship_Flags::Secondaries_locked]) {
|
|
m_secondaries_locked = 1;
|
|
}
|
|
else {
|
|
m_secondaries_locked = 0;
|
|
}
|
|
|
|
//Lock turrets
|
|
if (Ships[m_ship].flags[Ship::Ship_Flags::Lock_all_turrets_initially]) {
|
|
m_turrets_locked = 1;
|
|
}
|
|
else {
|
|
m_turrets_locked = 0;
|
|
}
|
|
|
|
if (Ships[m_ship].flags[Ship::Ship_Flags::Afterburner_locked]) {
|
|
m_afterburner_locked = 1;
|
|
}
|
|
else {
|
|
m_afterburner_locked = 0;
|
|
}
|
|
|
|
|
|
|
|
if (m_multi_edit) {
|
|
objp = GET_FIRST(&obj_used_list);
|
|
while (objp != END_OF_LIST(&obj_used_list)) {
|
|
if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && (objp->flags[Object::Object_Flags::Marked])) {
|
|
if (objp->phys_info.speed != m_velocity)
|
|
vflag = 1;
|
|
if ((int) objp->shield_quadrant[0] != m_shields)
|
|
sflag = 1;
|
|
if ((int) objp->hull_strength != m_hull)
|
|
hflag = 1;
|
|
if (objp->flags[Object::Object_Flags::No_shields]) {
|
|
if (m_has_shields)
|
|
m_has_shields = 2;
|
|
|
|
} else {
|
|
if (!m_has_shields)
|
|
m_has_shields = 2;
|
|
}
|
|
|
|
Assert((objp->type == OBJ_SHIP) || (objp->type == OBJ_START));
|
|
|
|
if (Ships[get_ship_from_obj(objp)].flags[Ship::Ship_Flags::Force_shields_on]) {
|
|
if (!m_force_shields)
|
|
m_force_shields = 2;
|
|
|
|
} else {
|
|
if (m_force_shields)
|
|
m_force_shields = 2;
|
|
}
|
|
|
|
if (Ships[get_ship_from_obj(objp)].flags[Ship::Ship_Flags::Ship_locked]) {
|
|
if (!m_ship_locked)
|
|
m_ship_locked = 2;
|
|
|
|
} else {
|
|
if (m_ship_locked)
|
|
m_ship_locked = 2;
|
|
}
|
|
|
|
if (Ships[get_ship_from_obj(objp)].flags[Ship::Ship_Flags::Weapons_locked]) {
|
|
if (!m_weapons_locked)
|
|
m_weapons_locked = 2;
|
|
|
|
} else {
|
|
if (m_weapons_locked)
|
|
m_weapons_locked = 2;
|
|
}
|
|
|
|
if (Ships[get_ship_from_obj(objp)].flags[Ship::Ship_Flags::Primaries_locked]){
|
|
if (!m_primaries_locked)
|
|
m_primaries_locked = 2;
|
|
}
|
|
else {
|
|
if (m_primaries_locked)
|
|
m_primaries_locked = 2;
|
|
}
|
|
|
|
if (Ships[get_ship_from_obj(objp)].flags[Ship::Ship_Flags::Secondaries_locked]){
|
|
if (!m_secondaries_locked)
|
|
m_secondaries_locked = 2;
|
|
}
|
|
else {
|
|
if (m_secondaries_locked)
|
|
m_secondaries_locked = 2;
|
|
}
|
|
|
|
if (Ships[get_ship_from_obj(objp)].flags[Ship::Ship_Flags::Lock_all_turrets_initially]){
|
|
if (!m_turrets_locked)
|
|
m_turrets_locked = 2;
|
|
}
|
|
else {
|
|
if (m_turrets_locked)
|
|
m_turrets_locked = 2;
|
|
}
|
|
|
|
if (Ships[get_ship_from_obj(objp)].flags[Ship::Ship_Flags::Afterburner_locked]){
|
|
if (!m_afterburner_locked)
|
|
m_afterburner_locked = 2;
|
|
}
|
|
else {
|
|
if (m_afterburner_locked)
|
|
m_afterburner_locked = 2;
|
|
}
|
|
}
|
|
|
|
objp = GET_NEXT(objp);
|
|
}
|
|
}
|
|
|
|
CDialog::OnInitDialog();
|
|
str.Format("%d", m_velocity);
|
|
GetDlgItem(IDC_VELOCITY)->SetWindowText(str);
|
|
str.Format("%d", m_shields);
|
|
GetDlgItem(IDC_SHIELDS)->SetWindowText(str);
|
|
str.Format("%d", m_hull);
|
|
GetDlgItem(IDC_HULL)->SetWindowText(str);
|
|
|
|
inited = 1;
|
|
|
|
GetDlgItem(IDC_SHIELDS)->EnableWindow(m_has_shields ? TRUE : FALSE);
|
|
GetDlgItem(IDC_SHIELDS_SPIN)->EnableWindow(m_has_shields ? TRUE : FALSE);
|
|
|
|
m_velocity_spin.SetRange(0, 100);
|
|
m_hull_spin.SetRange(0, 100);
|
|
m_shields_spin.SetRange(0, 100);
|
|
m_damage_spin.SetRange(0, 100);
|
|
|
|
// init boxes
|
|
lstDockerPoints = (CListBox *) GetDlgItem(IDC_DOCKER_POINT);
|
|
cboDockees = (CComboBox *) GetDlgItem(IDC_DOCKEE);
|
|
cboDockeePoints = (CComboBox *) GetDlgItem(IDC_DOCKEE_POINT);
|
|
lstDockerPoints->ResetContent();
|
|
cboDockees->ResetContent();
|
|
cboDockeePoints->ResetContent();
|
|
|
|
if (!m_multi_edit)
|
|
{
|
|
for (int dockpoint = 0; dockpoint < num_dock_points; dockpoint++)
|
|
{
|
|
z = lstDockerPoints->AddString(model_get_dock_name(Ship_info[Ships[m_ship].ship_info_index].model_num, dockpoint));
|
|
lstDockerPoints->SetItemData(z, dockpoint);
|
|
}
|
|
|
|
for (ptr = GET_FIRST(&Ships[m_ship].subsys_list); ptr != END_OF_LIST(&Ships[m_ship].subsys_list); ptr = GET_NEXT(ptr))
|
|
{
|
|
((CListBox *) GetDlgItem(IDC_SUBSYS))->AddString(ptr->system_info->subobj_name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GetDlgItem(IDC_SUBSYS)->EnableWindow(FALSE);
|
|
GetDlgItem(IDC_DAMAGE)->EnableWindow(FALSE);
|
|
}
|
|
change_docker_point(false);
|
|
change_subsys();
|
|
|
|
UpdateData(FALSE);
|
|
|
|
if (vflag)
|
|
GetDlgItem(IDC_VELOCITY)->SetWindowText("");
|
|
if (sflag)
|
|
GetDlgItem(IDC_SHIELDS)->SetWindowText("");
|
|
if (hflag)
|
|
GetDlgItem(IDC_HULL)->SetWindowText("");
|
|
|
|
if (objp != NULL) {
|
|
if (objp->type == OBJ_SHIP || objp->type == OBJ_START) {
|
|
ship* shipp = &Ships[objp->instance];
|
|
int i = 0;
|
|
|
|
if (Ship_info[shipp->ship_info_index].uses_team_colors) {
|
|
//Add a "None" entry at the beginning to allow simple deselection of colours
|
|
int t = m_team_color_setting.AddString("None");
|
|
m_team_color_setting.SetCurSel(i);
|
|
m_team_color_setting.SetItemData(t, i);
|
|
++i;
|
|
|
|
for (SCP_vector<SCP_string>::iterator tni = Team_Names.begin(); tni != Team_Names.end(); ++tni) {
|
|
z = m_team_color_setting.AddString(tni->c_str());
|
|
if (!stricmp(tni->c_str(), shipp->team_name.c_str())) {
|
|
m_team_color_setting.SetCurSel(i);
|
|
}
|
|
m_team_color_setting.SetItemData(z, i);
|
|
i++;
|
|
|
|
}
|
|
m_team_color_setting.EnableWindow();
|
|
} else {
|
|
m_team_color_setting.EnableWindow(FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void initial_status::OnOK()
|
|
{
|
|
char buf[256];
|
|
int vflag = 0, sflag = 0, hflag = 0;
|
|
object *objp;
|
|
|
|
if (GetDlgItem(IDC_VELOCITY)->GetWindowText(buf, 255))
|
|
vflag = 1;
|
|
if (GetDlgItem(IDC_SHIELDS)->GetWindowText(buf, 255))
|
|
sflag = 1;
|
|
if (GetDlgItem(IDC_HULL)->GetWindowText(buf, 255))
|
|
hflag = 1;
|
|
|
|
UpdateData(TRUE);
|
|
|
|
change_subsys();
|
|
|
|
if (m_multi_edit) {
|
|
objp = GET_FIRST(&obj_used_list);
|
|
while (objp != END_OF_LIST(&obj_used_list)) {
|
|
if (((objp->type == OBJ_SHIP) || (objp->type == OBJ_START)) && (objp->flags[Object::Object_Flags::Marked])) {
|
|
if (vflag)
|
|
MODIFY(objp->phys_info.speed, (float) m_velocity);
|
|
|
|
if (sflag)
|
|
MODIFY(objp->shield_quadrant[0], (float) m_shields);
|
|
|
|
if (hflag)
|
|
MODIFY(objp->hull_strength, (float) m_hull);
|
|
|
|
if (m_has_shields == 1)
|
|
objp->flags.remove(Object::Object_Flags::No_shields);
|
|
else if (m_has_shields == 0)
|
|
objp->flags.set(Object::Object_Flags::No_shields);
|
|
|
|
auto shipp = &Ships[get_ship_from_obj(objp)];
|
|
|
|
// We need to ensure that we handle the inconsistent "boolean" value correctly
|
|
handle_inconsistent_flag(shipp->flags, Ship::Ship_Flags::Force_shields_on, m_force_shields);
|
|
handle_inconsistent_flag(shipp->flags, Ship::Ship_Flags::Ship_locked, m_ship_locked);
|
|
handle_inconsistent_flag(shipp->flags, Ship::Ship_Flags::Weapons_locked, m_weapons_locked);
|
|
handle_inconsistent_flag(shipp->flags, Ship::Ship_Flags::Primaries_locked, m_primaries_locked);
|
|
handle_inconsistent_flag(shipp->flags, Ship::Ship_Flags::Secondaries_locked, m_secondaries_locked);
|
|
handle_inconsistent_flag(shipp->flags, Ship::Ship_Flags::Lock_all_turrets_initially, m_turrets_locked);
|
|
handle_inconsistent_flag(shipp->flags, Ship::Ship_Flags::Afterburner_locked, m_afterburner_locked);
|
|
}
|
|
|
|
objp = GET_NEXT(objp);
|
|
}
|
|
|
|
} else {
|
|
MODIFY(Objects[cur_object_index].phys_info.speed, (float) m_velocity);
|
|
MODIFY(Objects[cur_object_index].shield_quadrant[0], (float) m_shields);
|
|
MODIFY(Objects[cur_object_index].hull_strength, (float) m_hull);
|
|
|
|
Objects[cur_object_index].flags.set(Object::Object_Flags::No_shields, m_has_shields == 0);
|
|
|
|
// We need to ensure that we handle the inconsistent "boolean" value correctly. Not strictly needed here but just to be safe...
|
|
handle_inconsistent_flag(Ships[m_ship].flags, Ship::Ship_Flags::Force_shields_on, m_force_shields);
|
|
handle_inconsistent_flag(Ships[m_ship].flags, Ship::Ship_Flags::Ship_locked, m_ship_locked);
|
|
handle_inconsistent_flag(Ships[m_ship].flags, Ship::Ship_Flags::Weapons_locked, m_weapons_locked);
|
|
handle_inconsistent_flag(Ships[m_ship].flags, Ship::Ship_Flags::Primaries_locked, m_primaries_locked);
|
|
handle_inconsistent_flag(Ships[m_ship].flags, Ship::Ship_Flags::Secondaries_locked, m_secondaries_locked);
|
|
handle_inconsistent_flag(Ships[m_ship].flags, Ship::Ship_Flags::Lock_all_turrets_initially, m_turrets_locked);
|
|
handle_inconsistent_flag(Ships[m_ship].flags, Ship::Ship_Flags::Afterburner_locked, m_afterburner_locked);
|
|
}
|
|
|
|
if (m_team_color_setting.IsWindowEnabled()) {
|
|
if (m_team_color_setting.GetCurSel() > 0)
|
|
Ships[m_ship].team_name = Team_Names[m_team_color_setting.GetCurSel() - 1];
|
|
else
|
|
Ships[m_ship].team_name = "none";
|
|
}
|
|
|
|
update_docking_info();
|
|
|
|
CDialog::OnOK();
|
|
}
|
|
|
|
void initial_status::OnHasShields()
|
|
{
|
|
if (m_has_shields == 1) {
|
|
m_has_shields = 0;
|
|
|
|
// can't force shields and also have them off
|
|
if (m_force_shields) {
|
|
m_force_shields = 0;
|
|
((CButton *) GetDlgItem(IDC_FORCE_SHIELDS))->SetCheck(m_force_shields);
|
|
|
|
// warn on multiple ships of different state
|
|
if (m_multi_edit) {
|
|
MessageBox("At least one selected ship was set to Force Shields On. This is now turned off for all selected ships", "Resetting Flag", MB_OK | MB_ICONEXCLAMATION);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
m_has_shields = 1;
|
|
|
|
((CButton *) GetDlgItem(IDC_HAS_SHIELDS))->SetCheck(m_has_shields);
|
|
GetDlgItem(IDC_SHIELDS)->EnableWindow(m_has_shields);
|
|
GetDlgItem(IDC_SHIELDS_SPIN)->EnableWindow(m_has_shields);
|
|
}
|
|
|
|
void initial_status::OnForceShields()
|
|
{
|
|
if (m_force_shields == 1)
|
|
m_force_shields = 0;
|
|
else {
|
|
m_force_shields = 1;
|
|
|
|
// can't force shields and also turn have them off
|
|
if (m_has_shields != 1) {
|
|
m_has_shields = 1;
|
|
((CButton *) GetDlgItem(IDC_HAS_SHIELDS))->SetCheck(m_has_shields);
|
|
GetDlgItem(IDC_SHIELDS)->EnableWindow(m_has_shields);
|
|
GetDlgItem(IDC_SHIELDS_SPIN)->EnableWindow(m_has_shields);
|
|
|
|
// warn on multiple ships of different state
|
|
if (m_multi_edit) {
|
|
MessageBox("At least one selected ship was set to have no shields. Shields are now enabled for all selected ships", "Resetting Flag", MB_OK | MB_ICONEXCLAMATION);
|
|
}
|
|
}
|
|
}
|
|
|
|
((CButton *) GetDlgItem(IDC_FORCE_SHIELDS))->SetCheck(m_force_shields);
|
|
}
|
|
|
|
void initial_status::OnShipLocked()
|
|
{
|
|
if (m_ship_locked == 1)
|
|
m_ship_locked = 0;
|
|
else
|
|
m_ship_locked = 1;
|
|
|
|
((CButton *) GetDlgItem(IDC_SHIP_LOCKED))->SetCheck(m_ship_locked);
|
|
}
|
|
|
|
void initial_status::OnWeaponsLocked()
|
|
{
|
|
if (m_weapons_locked == 1)
|
|
m_weapons_locked = 0;
|
|
else
|
|
m_weapons_locked = 1;
|
|
|
|
((CButton *) GetDlgItem(IDC_WEAPONS_LOCKED))->SetCheck(m_weapons_locked);
|
|
}
|
|
|
|
void initial_status::OnSelchangeSubsys()
|
|
{
|
|
UpdateData(TRUE);
|
|
change_subsys();
|
|
UpdateData(FALSE);
|
|
}
|
|
|
|
void initial_status::change_subsys()
|
|
{
|
|
int z, cargo_index, enable = FALSE, enable_cargo_name = FALSE;
|
|
int ship_has_scannable_subsystems;
|
|
ship_subsys *ptr;
|
|
|
|
// Goober5000
|
|
ship_has_scannable_subsystems = Ship_info[Ships[m_ship].ship_info_index].is_huge_ship();
|
|
if (Ships[m_ship].flags[Ship::Ship_Flags::Toggle_subsystem_scanning])
|
|
ship_has_scannable_subsystems = !ship_has_scannable_subsystems;
|
|
|
|
if (cur_subsys != LB_ERR) {
|
|
ptr = GET_FIRST(&Ships[m_ship].subsys_list);
|
|
while (cur_subsys--) {
|
|
Assert(ptr != END_OF_LIST(&Ships[m_ship].subsys_list));
|
|
ptr = GET_NEXT(ptr);
|
|
}
|
|
|
|
MODIFY(ptr -> current_hits, 100.0f - (float) m_damage);
|
|
|
|
// update cargo name
|
|
if (strlen(m_cargo_name) > 0) { //-V805
|
|
lcl_fred_replace_stuff(m_cargo_name);
|
|
cargo_index = string_lookup(m_cargo_name, Cargo_names, Num_cargo);
|
|
if (cargo_index == -1) {
|
|
if (Num_cargo < MAX_CARGO);
|
|
cargo_index = Num_cargo++;
|
|
strcpy(Cargo_names[cargo_index], m_cargo_name);
|
|
ptr->subsys_cargo_name = cargo_index;
|
|
} else {
|
|
ptr->subsys_cargo_name = cargo_index;
|
|
}
|
|
} else {
|
|
ptr->subsys_cargo_name = 0;
|
|
}
|
|
set_modified();
|
|
}
|
|
|
|
cur_subsys = z = ((CListBox *) GetDlgItem(IDC_SUBSYS)) -> GetCurSel();
|
|
if (z == LB_ERR) {
|
|
m_damage = 100;
|
|
|
|
} else {
|
|
ptr = GET_FIRST(&Ships[m_ship].subsys_list);
|
|
while (z--) {
|
|
Assert(ptr != END_OF_LIST(&Ships[m_ship].subsys_list));
|
|
ptr = GET_NEXT(ptr);
|
|
}
|
|
|
|
m_damage = 100 - (int) ptr -> current_hits;
|
|
if ( ship_has_scannable_subsystems ) {
|
|
enable_cargo_name = TRUE;
|
|
if (ptr->subsys_cargo_name > 0) {
|
|
m_cargo_name = Cargo_names[ptr->subsys_cargo_name];
|
|
} else {
|
|
m_cargo_name = _T("");
|
|
}
|
|
} else {
|
|
m_cargo_name = _T("");
|
|
}
|
|
enable = TRUE;
|
|
}
|
|
|
|
GetDlgItem(IDC_DAMAGE) -> EnableWindow(enable);
|
|
GetDlgItem(IDC_DAMAGE_SPIN) -> EnableWindow(enable);
|
|
GetDlgItem(IDC_CARGO_NAME)->EnableWindow(enable && enable_cargo_name);
|
|
}
|
|
|
|
void initial_status::OnSelchangeDockerPoint()
|
|
{
|
|
change_docker_point(true);
|
|
}
|
|
|
|
void initial_status::change_docker_point(bool store_selection)
|
|
{
|
|
int sel;
|
|
|
|
// grab from controls
|
|
UpdateData(TRUE);
|
|
|
|
// get new selection
|
|
sel = lstDockerPoints->GetCurSel();
|
|
if (sel != LB_ERR)
|
|
cur_docker_point = (int)lstDockerPoints->GetItemData(sel);
|
|
else
|
|
cur_docker_point = -1;
|
|
|
|
// populate the next dropdowns
|
|
if (cur_docker_point < 0)
|
|
{
|
|
// clear the dropdowns
|
|
list_dockees(-1);
|
|
list_dockee_points(-1);
|
|
}
|
|
else
|
|
{
|
|
// populate with all possible dockees
|
|
list_dockees(model_get_dock_index_type(Ship_info[Ships[m_ship].ship_info_index].model_num, cur_docker_point));
|
|
|
|
// see if there's a dockee here
|
|
if (dockpoint_array[cur_docker_point].dockee_shipnum >= 0)
|
|
{
|
|
// select the dockee
|
|
cboDockees->SelectString(-1, Ships[dockpoint_array[cur_docker_point].dockee_shipnum].ship_name);
|
|
change_dockee(false);
|
|
}
|
|
else
|
|
{
|
|
// select "Nothing"
|
|
cboDockees->SetCurSel(0);
|
|
change_dockee(true);
|
|
}
|
|
}
|
|
|
|
// store to controls
|
|
UpdateData(FALSE);
|
|
}
|
|
|
|
void initial_status::OnSelchangeDockee()
|
|
{
|
|
change_dockee(true);
|
|
}
|
|
|
|
void initial_status::change_dockee(bool store_selection)
|
|
{
|
|
int sel;
|
|
|
|
// grab from controls
|
|
UpdateData(TRUE);
|
|
|
|
// get new selection
|
|
sel = cboDockees->GetCurSel();
|
|
if (sel != CB_ERR)
|
|
cur_dockee = (int)cboDockees->GetItemData(sel);
|
|
else
|
|
cur_dockee = -1;
|
|
|
|
// are we storing it?
|
|
if (store_selection)
|
|
{
|
|
dockpoint_array[cur_docker_point].dockee_shipnum = cur_dockee;
|
|
dockpoint_array[cur_docker_point].dockee_point = -1;
|
|
}
|
|
|
|
// populate the next dropdown
|
|
if (cur_dockee < 0)
|
|
{
|
|
// clear the dropdown
|
|
list_dockee_points(-1);
|
|
}
|
|
else
|
|
{
|
|
// populate with dockee points
|
|
list_dockee_points(cur_dockee);
|
|
|
|
// see if there's a dockpoint here
|
|
if (dockpoint_array[cur_docker_point].dockee_point >= 0)
|
|
{
|
|
// select the dockpoint
|
|
cboDockeePoints->SelectString(-1, model_get_dock_name(Ship_info[Ships[cur_dockee].ship_info_index].model_num, dockpoint_array[cur_docker_point].dockee_point));
|
|
change_dockee_point(false);
|
|
}
|
|
// there might not be any dockpoints available
|
|
else if (cboDockeePoints->GetCount() == 0)
|
|
{
|
|
cboDockeePoints->EnableWindow(FALSE);
|
|
change_dockee_point(false);
|
|
}
|
|
else
|
|
{
|
|
// just select the first dockpoint
|
|
cboDockeePoints->SetCurSel(0);
|
|
change_dockee_point(true);
|
|
}
|
|
}
|
|
|
|
// store to controls
|
|
UpdateData(FALSE);
|
|
}
|
|
|
|
void initial_status::OnSelchangeDockeePoint()
|
|
{
|
|
change_dockee_point(true);
|
|
}
|
|
|
|
void initial_status::change_dockee_point(bool store_selection)
|
|
{
|
|
int sel;
|
|
|
|
// grab from controls
|
|
UpdateData(TRUE);
|
|
|
|
// get new value
|
|
sel = cboDockeePoints->GetCurSel();
|
|
if (sel != CB_ERR)
|
|
cur_dockee_point = (int)cboDockeePoints->GetItemData(sel);
|
|
else
|
|
cur_dockee_point = -1;
|
|
|
|
// are we storing it?
|
|
if (store_selection)
|
|
{
|
|
dockpoint_array[cur_docker_point].dockee_point = cur_dockee_point;
|
|
}
|
|
|
|
// store to controls
|
|
UpdateData(FALSE);
|
|
}
|
|
|
|
void initial_status::list_dockees(int dock_types)
|
|
{
|
|
int z;
|
|
|
|
// enable/disable dropdown
|
|
cboDockees->EnableWindow((dock_types >= 0) ? TRUE : FALSE);
|
|
|
|
// clear the existing dockees
|
|
cboDockees->ResetContent();
|
|
|
|
// that might be all we need to do
|
|
if (dock_types < 0)
|
|
return;
|
|
|
|
// populate with potential dockees
|
|
|
|
// add "nothing"
|
|
z = cboDockees->AddString("Nothing");
|
|
cboDockees->SetItemData(z, static_cast<DWORD_PTR>(-1));
|
|
|
|
// add ships
|
|
for (object *objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp))
|
|
{
|
|
if ((objp->type == OBJ_SHIP) || (objp->type == OBJ_START))
|
|
{
|
|
int ship = get_ship_from_obj(objp);
|
|
|
|
// mustn't be the same ship
|
|
if (ship == m_ship)
|
|
continue;
|
|
|
|
// mustn't also be docked elsewhere
|
|
bool docked_elsewhere = false;
|
|
for (int i = 0; i < num_dock_points; i++)
|
|
{
|
|
// don't erroneously check the same point
|
|
if (i == cur_docker_point)
|
|
continue;
|
|
|
|
// see if this ship is also on a different dockpoint
|
|
if (dockpoint_array[i].dockee_shipnum == ship)
|
|
{
|
|
docked_elsewhere = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (docked_elsewhere)
|
|
continue;
|
|
|
|
// docking must be valid
|
|
if (!ship_docking_valid(m_ship, ship) && !ship_docking_valid(ship, m_ship))
|
|
continue;
|
|
|
|
// dock types must match
|
|
if (!(model_get_dock_types(Ship_info[Ships[ship].ship_info_index].model_num) & dock_types))
|
|
continue;
|
|
|
|
// add to list
|
|
z = cboDockees->AddString(Ships[ship].ship_name);
|
|
cboDockees->SetItemData(z, ship);
|
|
}
|
|
}
|
|
}
|
|
|
|
void initial_status::list_dockee_points(int shipnum)
|
|
{
|
|
int z;
|
|
ship *shipp = &Ships[m_ship];
|
|
ship *other_shipp = &Ships[shipnum];
|
|
|
|
// enable/disable dropdown
|
|
cboDockeePoints->EnableWindow((shipnum >= 0) ? TRUE : FALSE);
|
|
|
|
// clear the existing dockee points
|
|
cboDockeePoints->ResetContent();
|
|
|
|
// that might be all we need to do
|
|
if (shipnum < 0)
|
|
return;
|
|
|
|
// get the required dock type(s)
|
|
int dock_type = model_get_dock_index_type(Ship_info[shipp->ship_info_index].model_num, cur_docker_point);
|
|
|
|
// populate with the right kind of dockee points
|
|
for (int i = 0; i < model_get_num_dock_points(Ship_info[other_shipp->ship_info_index].model_num); i++)
|
|
{
|
|
// make sure this dockpoint is not occupied by someone else
|
|
object *docked_objp = dock_find_object_at_dockpoint(&Objects[other_shipp->objnum], i);
|
|
if ((docked_objp != NULL) && (docked_objp != &Objects[shipp->objnum]))
|
|
continue;
|
|
|
|
// make sure its type matches
|
|
if (!(model_get_dock_index_type(Ship_info[other_shipp->ship_info_index].model_num, i) & dock_type))
|
|
continue;
|
|
|
|
// add to list
|
|
z = cboDockeePoints->AddString(model_get_dock_name(Ship_info[other_shipp->ship_info_index].model_num, i));
|
|
cboDockeePoints->SetItemData(z, i);
|
|
}
|
|
}
|
|
|
|
void initial_status::update_docking_info()
|
|
{
|
|
int i;
|
|
object *objp = &Objects[Ships[m_ship].objnum];
|
|
|
|
// remove old info
|
|
for (i = 0; i < num_dock_points; i++)
|
|
{
|
|
// see if the object at this point is no longer there
|
|
object *dockee_objp = dock_find_object_at_dockpoint(objp, i);
|
|
if (dockee_objp != NULL)
|
|
{
|
|
// check if the dockee ship thinks that this ship is docked to this dock point
|
|
if (objp != dock_find_object_at_dockpoint(dockee_objp, dockpoint_array[i].dockee_point) ) {
|
|
// undock it
|
|
undock(objp, dockee_objp);
|
|
}
|
|
}
|
|
}
|
|
|
|
// add new info
|
|
for (i = 0; i < num_dock_points; i++)
|
|
{
|
|
// see if there is an object at this point that wasn't there before
|
|
if (dockpoint_array[i].dockee_shipnum >= 0)
|
|
{
|
|
if (dock_find_object_at_dockpoint(objp, i) == NULL)
|
|
{
|
|
object *dockee_objp = &Objects[Ships[dockpoint_array[i].dockee_shipnum].objnum];
|
|
int dockee_point = dockpoint_array[i].dockee_point;
|
|
|
|
// dock it
|
|
dock(objp, i, dockee_objp, dockee_point);
|
|
}
|
|
}
|
|
}
|
|
|
|
update_map_window();
|
|
}
|
|
|
|
void initial_status::dock(object *objp, int dockpoint, object *other_objp, int other_dockpoint)
|
|
{
|
|
if (objp == NULL || other_objp == NULL)
|
|
return;
|
|
|
|
if (dockpoint < 0 || other_dockpoint < 0)
|
|
return;
|
|
|
|
dock_function_info dfi;
|
|
|
|
// do the docking (do it in reverse so that the current object stays put)
|
|
ai_dock_with_object(other_objp, other_dockpoint, objp, dockpoint, AIDO_DOCK_NOW);
|
|
|
|
// unmark the handled flag in preparation for the next step
|
|
dock_evaluate_all_docked_objects(objp, &dfi, initial_status_unmark_dock_handled_flag);
|
|
|
|
// move all other objects to catch up with it
|
|
dock_move_docked_objects(objp);
|
|
|
|
// set the dock leader
|
|
dock_evaluate_all_docked_objects(objp, &dfi, initial_status_mark_dock_leader_helper);
|
|
|
|
// if no leader, mark me
|
|
if (dfi.maintained_variables.int_value == 0)
|
|
Ships[objp->instance].flags.set(Ship::Ship_Flags::Dock_leader);
|
|
}
|
|
|
|
void initial_status::undock(object *objp1, object *objp2)
|
|
{
|
|
vec3d v;
|
|
int ship_num, other_ship_num;
|
|
|
|
if (objp1 == NULL || objp2 == NULL)
|
|
return;
|
|
|
|
vm_vec_sub(&v, &objp2->pos, &objp1->pos);
|
|
vm_vec_normalize(&v);
|
|
ship_num = get_ship_from_obj(OBJ_INDEX(objp1));
|
|
other_ship_num = get_ship_from_obj(OBJ_INDEX(objp2));
|
|
|
|
if (Move_ships_when_undocking)
|
|
{
|
|
if (ship_class_compare(Ships[ship_num].ship_info_index, Ships[other_ship_num].ship_info_index) <= 0)
|
|
vm_vec_scale_add2(&objp2->pos, &v, ship_class_get_length(&Ship_info[Ships[objp2->instance].ship_info_index]));
|
|
else
|
|
vm_vec_scale_add2(&objp1->pos, &v, ship_class_get_length(&Ship_info[Ships[objp1->instance].ship_info_index]) * -1.0f);
|
|
}
|
|
|
|
ai_do_objects_undocked_stuff(objp1, objp2);
|
|
|
|
// check to see if one of these ships has an arrival cue of false. If so, then
|
|
// reset it back to default value of true. be sure to correctly update before
|
|
// and after setting data.
|
|
// Goober5000 - but don't reset it if it's part of a wing!
|
|
Ship_editor_dialog.update_data(1);
|
|
|
|
if ( Ships[ship_num].arrival_cue == Locked_sexp_false && Ships[ship_num].wingnum < 0 ) {
|
|
Ships[ship_num].arrival_cue = Locked_sexp_true;
|
|
} else if ( Ships[other_ship_num].arrival_cue == Locked_sexp_false && Ships[other_ship_num].wingnum < 0 ) {
|
|
Ships[other_ship_num].arrival_cue = Locked_sexp_true;
|
|
}
|
|
|
|
// if this ship is no longer docked, ensure its dock leader flag is clear
|
|
if (!object_is_docked(&Objects[Ships[ship_num].objnum]))
|
|
Ships[ship_num].flags.remove(Ship::Ship_Flags::Dock_leader);
|
|
|
|
// same for the other ship
|
|
if (!object_is_docked(&Objects[Ships[other_ship_num].objnum]))
|
|
Ships[other_ship_num].flags.remove(Ship::Ship_Flags::Dock_leader);
|
|
|
|
Ship_editor_dialog.initialize_data(1);
|
|
}
|
|
|
|
// ----- the following are not contained in the class because of apparent scope issues -----
|
|
|
|
bool set_cue_to_false(int *cue)
|
|
{
|
|
// if the cue is not false, make it false. Be sure to set all ship editor dialog functions
|
|
// to update data before and after we modify the cue.
|
|
if (*cue != Locked_sexp_false)
|
|
{
|
|
Ship_editor_dialog.update_data(1);
|
|
Wing_editor_dialog.update_data(1);
|
|
|
|
free_sexp2(*cue);
|
|
*cue = Locked_sexp_false;
|
|
|
|
Ship_editor_dialog.initialize_data(1);
|
|
Wing_editor_dialog.initialize_data(1);
|
|
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
// function to set the arrival cue of a ship to false
|
|
void reset_arrival_to_false(int shipnum, bool reset_wing)
|
|
{
|
|
char buf[256];
|
|
ship *shipp = &Ships[shipnum];
|
|
|
|
// falsify the ship cue
|
|
if (set_cue_to_false(&shipp->arrival_cue))
|
|
{
|
|
sprintf(buf, "Setting arrival cue of ship %s\nto false for initial docking purposes.", shipp->ship_name);
|
|
MessageBox(NULL, buf, "", MB_OK | MB_ICONEXCLAMATION);
|
|
}
|
|
|
|
// falsify the wing cue and all ships in that wing
|
|
if (reset_wing && shipp->wingnum >= 0)
|
|
{
|
|
int i;
|
|
wing *wingp = &Wings[shipp->wingnum];
|
|
|
|
if (set_cue_to_false(&wingp->arrival_cue))
|
|
{
|
|
sprintf(buf, "Setting arrival cue of wing %s\nto false for initial docking purposes.", wingp->name);
|
|
MessageBox(NULL, buf, "", MB_OK | MB_ICONEXCLAMATION);
|
|
}
|
|
|
|
for (i = 0; i < wingp->wave_count; i++)
|
|
reset_arrival_to_false(wingp->ship_index[i], false);
|
|
}
|
|
}
|
|
|
|
// NOTE - in both retail and SCP, the dock "leader" is defined as the only guy in his
|
|
// group with a non-false arrival cue
|
|
void initial_status_mark_dock_leader_helper(object *objp, dock_function_info *infop)
|
|
{
|
|
ship *shipp = &Ships[objp->instance];
|
|
int cue_to_check;
|
|
|
|
// if this guy is part of a wing, he uses his wing's arrival cue
|
|
if (shipp->wingnum >= 0)
|
|
{
|
|
cue_to_check = Wings[shipp->wingnum].arrival_cue;
|
|
}
|
|
// check the ship's arrival cue
|
|
else
|
|
{
|
|
cue_to_check = shipp->arrival_cue;
|
|
}
|
|
|
|
// all ships except the leader should have a locked false arrival cue
|
|
if (cue_to_check != Locked_sexp_false)
|
|
{
|
|
object *existing_leader;
|
|
|
|
// increment number of leaders found
|
|
infop->maintained_variables.int_value++;
|
|
|
|
// see if we already found a leader
|
|
existing_leader = infop->maintained_variables.objp_value;
|
|
if (existing_leader != NULL)
|
|
{
|
|
ship *leader_shipp = &Ships[existing_leader->instance];
|
|
|
|
// keep existing leader if he has a higher priority than us
|
|
if (ship_class_compare(shipp->ship_info_index, leader_shipp->ship_info_index) >= 0)
|
|
{
|
|
// set my arrival cue to false
|
|
reset_arrival_to_false(SHIP_INDEX(shipp), true);
|
|
return;
|
|
}
|
|
|
|
// otherwise, unmark the existing leader and set his arrival cue to false
|
|
leader_shipp->flags.remove(Ship::Ship_Flags::Dock_leader);
|
|
reset_arrival_to_false(SHIP_INDEX(leader_shipp), true);
|
|
}
|
|
|
|
// mark and save me as the leader
|
|
shipp->flags.set(Ship::Ship_Flags::Dock_leader);
|
|
infop->maintained_variables.objp_value = objp;
|
|
}
|
|
}
|
|
|
|
// self-explanatory, really
|
|
void initial_status_unmark_dock_handled_flag(object *objp, dock_function_info *infop)
|
|
{
|
|
objp->flags.remove(Object::Object_Flags::Docked_already_handled);
|
|
}
|
|
|
|
void initial_status::OnPrimariesLocked()
|
|
{
|
|
if (m_primaries_locked == 1)
|
|
m_primaries_locked = 0;
|
|
else
|
|
m_primaries_locked = 1;
|
|
|
|
((CButton *) GetDlgItem(IDC_PRIMARIES_LOCKED))->SetCheck(m_primaries_locked);
|
|
}
|
|
|
|
void initial_status::OnSecondariesLocked()
|
|
{
|
|
if (m_secondaries_locked == 1)
|
|
m_secondaries_locked = 0;
|
|
else
|
|
m_secondaries_locked = 1;
|
|
|
|
((CButton *) GetDlgItem(IDC_SECONDARIES_LOCKED))->SetCheck(m_secondaries_locked);
|
|
}
|
|
|
|
void initial_status::OnTurretsLocked()
|
|
{
|
|
if (m_turrets_locked == 1)
|
|
m_turrets_locked = 0;
|
|
else
|
|
m_turrets_locked = 1;
|
|
|
|
((CButton *) GetDlgItem(IDC_TURRETS_LOCKED))->SetCheck(m_turrets_locked);
|
|
}
|
|
|
|
void initial_status::OnAfterburnersLocked()
|
|
{
|
|
if (m_afterburner_locked == 1)
|
|
m_afterburner_locked = 0;
|
|
else
|
|
m_afterburner_locked = 1;
|
|
|
|
((CButton *) GetDlgItem(IDC_AFTERBURNER_LOCKED))->SetCheck(m_afterburner_locked);
|
|
}
|