mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
489 lines
12 KiB
C++
489 lines
12 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 "CreateWingDlg.h"
|
|
#include "FREDDoc.h"
|
|
#include "FREDView.h"
|
|
#include "MainFrm.h"
|
|
#include "Management.h"
|
|
#include "Management.h"
|
|
#include "wing.h"
|
|
|
|
#include "globalincs/linklist.h"
|
|
#include "math/vecmat.h"
|
|
#include "object/object.h"
|
|
#include "physics/physics.h"
|
|
#include "render/3d.h"
|
|
#include "ship/ship.h"
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
#define MULTI_WING 999999
|
|
|
|
int already_deleting_wing = 0;
|
|
int Wings_initialized = 0;
|
|
|
|
// Declared here
|
|
/**
|
|
* @brief Takes a player out of a wing, deleting wing if that was the only ship in it.
|
|
*/
|
|
void remove_player_from_wing(int player, int min = 1);
|
|
|
|
/**
|
|
* @brief Checks wing dependencies
|
|
*
|
|
* @TODO verify
|
|
*/
|
|
int check_wing_dependencies(int wing_num);
|
|
|
|
/**
|
|
* @brief Initializes the wings and formations
|
|
*
|
|
* @note Looks incomplete. Only init's the first formation
|
|
* @TODO verify
|
|
*/
|
|
void initialize_wings(void);
|
|
|
|
/**
|
|
* @brief Gets the index of the first available object slot
|
|
*
|
|
*
|
|
* @returns The objindex of the first available slot, or
|
|
* @returns -1 if no slots available
|
|
*
|
|
* @note Unused
|
|
* @TODO verify and maybe remove
|
|
*/
|
|
int get_free_objnum(void);
|
|
|
|
/**
|
|
* @brief Finds a free wing slot (i.e. unused)
|
|
*/
|
|
int find_free_wing();
|
|
|
|
|
|
int check_wing_dependencies(int wing_num) {
|
|
char *name;
|
|
|
|
name = Wings[wing_num].name;
|
|
return reference_handler(name, sexp_ref_type::WING, -1);
|
|
}
|
|
|
|
int create_wing() {
|
|
char msg[1024];
|
|
int i, ship, wing = -1, waypoints = 0, count = 0, illegal_ships = 0;
|
|
int leader, leader_team;
|
|
object *ptr;
|
|
create_wing_dlg dlg;
|
|
|
|
if (!query_valid_object())
|
|
return -1;
|
|
|
|
leader = cur_object_index;
|
|
ptr = GET_FIRST(&obj_used_list);
|
|
while (ptr != END_OF_LIST(&obj_used_list)) {
|
|
if (( (ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START) ) && (ptr->flags[Object::Object_Flags::Marked])) {
|
|
count++;
|
|
i = -1;
|
|
switch (ptr->type) {
|
|
case OBJ_SHIP:
|
|
case OBJ_START:
|
|
i = Ships[ptr->instance].wingnum;
|
|
break;
|
|
}
|
|
|
|
if (i >= 0) {
|
|
if (wing < 0)
|
|
wing = i;
|
|
else if (wing != i)
|
|
wing = MULTI_WING;
|
|
}
|
|
}
|
|
|
|
ptr = GET_NEXT(ptr);
|
|
}
|
|
|
|
if (count > MAX_SHIPS_PER_WING) {
|
|
sprintf(msg, "You have too many ships marked!\n"
|
|
"A wing is limited to %d ships total", MAX_SHIPS_PER_WING);
|
|
|
|
Fred_main_wnd->MessageBox(msg, "Error", MB_ICONEXCLAMATION);
|
|
return -1;
|
|
}
|
|
|
|
if ((wing >= 0) && (wing != MULTI_WING)) {
|
|
sprintf(msg, "Do you want to reform wing \"%s\"?", Wings[wing].name);
|
|
i = Fred_main_wnd->MessageBox(msg, "Query", MB_YESNOCANCEL);
|
|
if (i == IDCANCEL)
|
|
return -1;
|
|
|
|
else if (i == IDNO)
|
|
wing = -1;
|
|
|
|
else { // must be IDYES
|
|
for (i = Wings[wing].wave_count - 1; i >= 0; i--) {
|
|
ptr = &Objects[wing_objects[wing][i]];
|
|
switch (ptr->type) {
|
|
case OBJ_SHIP:
|
|
remove_ship_from_wing(ptr->instance, 0);
|
|
break;
|
|
|
|
case OBJ_START:
|
|
remove_player_from_wing(ptr->instance, 0);
|
|
break;
|
|
|
|
default:
|
|
Int3(); // shouldn't be in a wing!
|
|
}
|
|
}
|
|
|
|
Assert(!Wings[wing].wave_count);
|
|
Num_wings--;
|
|
}
|
|
|
|
} else
|
|
wing = -1;
|
|
|
|
if (wing < 0) {
|
|
wing = find_free_wing();
|
|
|
|
if (wing < 0) {
|
|
Fred_main_wnd->MessageBox("Too many wings, can't create more!",
|
|
"Error", MB_ICONEXCLAMATION);
|
|
|
|
return -1;
|
|
}
|
|
|
|
Wings[wing].clear();
|
|
Wings[wing].arrival_cue = Locked_sexp_true;
|
|
Wings[wing].departure_cue = Locked_sexp_false;
|
|
|
|
if (dlg.DoModal() == IDCANCEL)
|
|
return -1;
|
|
|
|
dlg.m_name.TrimLeft();
|
|
dlg.m_name.TrimRight();
|
|
string_copy(Wings[wing].name, dlg.m_name, NAME_LENGTH - 1);
|
|
|
|
// if this name has a hash, create a default display name
|
|
if (get_pointer_to_first_hash_symbol(Wings[wing].name))
|
|
{
|
|
Wings[wing].display_name = Wings[wing].name;
|
|
end_string_at_first_hash_symbol(Wings[wing].display_name);
|
|
Wings[wing].flags.set(Ship::Wing_Flags::Has_display_name);
|
|
}
|
|
// otherwise reset the display name
|
|
else
|
|
{
|
|
Wings[wing].display_name = "";
|
|
Wings[wing].flags.remove(Ship::Wing_Flags::Has_display_name);
|
|
}
|
|
}
|
|
|
|
set_cur_indices(-1);
|
|
ptr = GET_FIRST(&obj_used_list);
|
|
while (ptr != END_OF_LIST(&obj_used_list)) {
|
|
if (ptr->flags[Object::Object_Flags::Marked]) {
|
|
// if ((ptr->type == OBJ_START) && (ptr->instance)) {
|
|
// starts++;
|
|
// unmark_object(OBJ_INDEX(ptr));
|
|
|
|
// } else if (ptr->type == OBJ_WAYPOINT) {
|
|
if (ptr->type == OBJ_WAYPOINT) {
|
|
waypoints++;
|
|
unmark_object(OBJ_INDEX(ptr));
|
|
|
|
} else if (ptr->type == OBJ_SHIP) {
|
|
int ship_type = ship_query_general_type(ptr->instance);
|
|
if (ship_type < 0 || !(Ship_types[ship_type].flags[Ship::Type_Info_Flags::AI_can_form_wing])) {
|
|
illegal_ships++;
|
|
unmark_object(OBJ_INDEX(ptr));
|
|
}
|
|
}
|
|
}
|
|
|
|
ptr = GET_NEXT(ptr);
|
|
}
|
|
|
|
// if this wing is a player starting wing, automatically set the hotkey for this wing
|
|
for (i = 0; i < MAX_STARTING_WINGS; i++) {
|
|
if (!stricmp(Wings[wing].name, Starting_wing_names[i])) {
|
|
Wings[wing].hotkey = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
count = 0;
|
|
if ((Player_start_shipnum >= 0) && (Player_start_shipnum < MAX_SHIPS) && (Ships[Player_start_shipnum].objnum >= 0)
|
|
&& Objects[Ships[Player_start_shipnum].objnum].flags[Object::Object_Flags::Marked])
|
|
count = 1;
|
|
|
|
ptr = GET_FIRST(&obj_used_list);
|
|
while (ptr != END_OF_LIST(&obj_used_list)) {
|
|
if (ptr->flags[Object::Object_Flags::Marked]) {
|
|
if ((ptr->type == OBJ_START) && (ptr->instance == Player_start_shipnum))
|
|
i = 0; // player 1 start always goes to front of the wing
|
|
else
|
|
i = count++;
|
|
|
|
Assert((ptr->type == OBJ_SHIP) || (ptr->type == OBJ_START));
|
|
ship = ptr->instance;
|
|
if (Ships[ship].wingnum != -1) {
|
|
if (ptr->type == OBJ_SHIP)
|
|
remove_ship_from_wing(ship);
|
|
else
|
|
remove_player_from_wing(ptr->instance);
|
|
}
|
|
|
|
wing_bash_ship_name(msg, Wings[wing].name, i + 1);
|
|
rename_ship(ship, msg);
|
|
// bash it again for the display name
|
|
wing_bash_ship_name(&Ships[ship], &Wings[wing], i + 1, true);
|
|
|
|
Wings[wing].ship_index[i] = ship;
|
|
Ships[ship].wingnum = wing;
|
|
if (Ships[ship].arrival_cue >= 0)
|
|
free_sexp2(Ships[ship].arrival_cue);
|
|
|
|
Ships[ship].arrival_cue = Locked_sexp_false;
|
|
if (Ships[ship].departure_cue >= 0)
|
|
free_sexp2(Ships[ship].departure_cue);
|
|
|
|
Ships[ship].departure_cue = Locked_sexp_false;
|
|
|
|
wing_objects[wing][i] = OBJ_INDEX(ptr);
|
|
if (OBJ_INDEX(ptr) == leader)
|
|
Wings[wing].special_ship = i;
|
|
}
|
|
|
|
ptr = GET_NEXT(ptr);
|
|
}
|
|
|
|
if (!count) // this should never happen, so if it does, needs to be fixed now.
|
|
Error(LOCATION, "No valid ships were selected to form wing from");
|
|
|
|
Wings[wing].wave_count = count;
|
|
Num_wings++;
|
|
|
|
// if (starts)
|
|
// Fred_main_wnd->MessageBox("Multi-player starting points can't be part of a wing!\n"
|
|
// "All marked multi-player starting points were ignored",
|
|
// "Error", MB_ICONEXCLAMATION);
|
|
|
|
if (waypoints)
|
|
Fred_main_wnd->MessageBox("Waypoints can't be part of a wing!\n"
|
|
"All marked waypoints were ignored",
|
|
"Error", MB_ICONEXCLAMATION);
|
|
|
|
if (illegal_ships)
|
|
Fred_main_wnd->MessageBox("Some ship types aren't allowed to be in a wing.\n"
|
|
"All marked ships of these types were ignored",
|
|
"Error", MB_ICONEXCLAMATION);
|
|
|
|
|
|
leader_team = Ships[Wings[wing].ship_index[Wings[wing].special_ship]].team;
|
|
for (i = 0; i < Wings[wing].wave_count; i++) {
|
|
if (Ships[Wings[wing].ship_index[i]].team != leader_team) {
|
|
Fred_main_wnd->MessageBox("Wing contains ships on different teams", "Warning");
|
|
break;
|
|
}
|
|
}
|
|
|
|
mark_wing(wing);
|
|
|
|
update_custom_wing_indexes();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int delete_wing(int wing_num, int bypass) {
|
|
int i, r, total;
|
|
|
|
if (already_deleting_wing)
|
|
return 0;
|
|
|
|
r = check_wing_dependencies(wing_num);
|
|
if (r)
|
|
return r;
|
|
|
|
already_deleting_wing = 1;
|
|
|
|
delete_reinforcement(Wings[wing_num].name);
|
|
|
|
invalidate_references(Wings[wing_num].name, sexp_ref_type::WING);
|
|
if (!bypass) {
|
|
total = Wings[wing_num].wave_count;
|
|
for (i = 0; i<total; i++)
|
|
delete_object(wing_objects[wing_num][i]);
|
|
}
|
|
|
|
Wings[wing_num].wave_count = 0;
|
|
Wings[wing_num].wing_squad_filename[0] = '\0';
|
|
Wings[wing_num].wing_insignia_texture = -1;
|
|
|
|
if (cur_wing == wing_num)
|
|
set_cur_wing(cur_wing = -1); // yes, one '=' is correct.
|
|
|
|
free_sexp2(Wings[wing_num].arrival_cue);
|
|
free_sexp2(Wings[wing_num].departure_cue);
|
|
|
|
Num_wings--;
|
|
set_modified();
|
|
|
|
update_custom_wing_indexes();
|
|
|
|
already_deleting_wing = 0;
|
|
return 0;
|
|
}
|
|
|
|
int find_free_wing()
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<MAX_WINGS; i++)
|
|
if (!Wings[i].wave_count)
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
int get_free_objnum(void) {
|
|
int i;
|
|
|
|
for (i = 1; i<MAX_OBJECTS; i++)
|
|
if (Objects[i].type == OBJ_NONE)
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
void initialize_wings(void) {
|
|
if (Wings_initialized)
|
|
return;
|
|
|
|
Wings_initialized = 1;
|
|
}
|
|
|
|
void mark_wing(int wing)
|
|
{
|
|
int i;
|
|
|
|
unmark_all();
|
|
Assert(Wings[wing].special_ship >= 0 && Wings[wing].special_ship < Wings[wing].wave_count);
|
|
set_cur_object_index(wing_objects[wing][Wings[wing].special_ship]);
|
|
for (i=0; i<Wings[wing].wave_count; i++)
|
|
mark_object(wing_objects[wing][i]);
|
|
}
|
|
|
|
void remove_player_from_wing(int player, int min) {
|
|
remove_ship_from_wing(player, min);
|
|
}
|
|
|
|
void remove_ship_from_wing(int ship, int min) {
|
|
char buf[256];
|
|
int i, wing, end, obj;
|
|
|
|
wing = Ships[ship].wingnum;
|
|
if (wing != -1) {
|
|
if (Wings[wing].wave_count == min) {
|
|
Wings[wing].wave_count = 0;
|
|
Wings[wing].wing_squad_filename[0] = '\0';
|
|
Wings[wing].wing_insignia_texture = -1;
|
|
delete_wing(wing);
|
|
} else {
|
|
i = Wings[wing].wave_count;
|
|
end = i - 1;
|
|
while (i--)
|
|
if (wing_objects[wing][i] == Ships[ship].objnum)
|
|
break;
|
|
|
|
Assert(i != -1); // Error, object should be in wing.
|
|
if (Wings[wing].special_ship == i)
|
|
Wings[wing].special_ship = 0;
|
|
|
|
// if not last element, move last element to position to fill gap
|
|
if (i != end) {
|
|
obj = wing_objects[wing][i] = wing_objects[wing][end];
|
|
Wings[wing].ship_index[i] = Wings[wing].ship_index[end];
|
|
if (Objects[obj].type == OBJ_SHIP) {
|
|
wing_bash_ship_name(buf, Wings[wing].name, i + 1);
|
|
rename_ship(Wings[wing].ship_index[i], buf);
|
|
// bash it again for the display name
|
|
wing_bash_ship_name(&Ships[Wings[wing].ship_index[i]], &Wings[wing], i + 1, true);
|
|
}
|
|
}
|
|
|
|
Wings[wing].wave_count--;
|
|
if (Wings[wing].wave_count) {
|
|
const auto max_threshold = MAX_SHIPS_PER_WING - Wings[wing].wave_count;
|
|
if (Wings[wing].threshold > max_threshold)
|
|
Wings[wing].threshold = max_threshold;
|
|
}
|
|
}
|
|
|
|
Ships[ship].wingnum = -1;
|
|
}
|
|
|
|
set_modified();
|
|
// reset ship name to non-wing default ship name
|
|
sprintf(buf, "%s %d", Ship_info[Ships[ship].ship_info_index].name, ship);
|
|
rename_ship(ship, buf);
|
|
}
|
|
|
|
void disband_wing(int wing_num)
|
|
{
|
|
int i, total;
|
|
object *ptr;
|
|
|
|
if (check_wing_dependencies(wing_num))
|
|
return;
|
|
|
|
delete_reinforcement(Wings[wing_num].name);
|
|
|
|
Ship_editor_dialog.bypass_errors = Wing_editor_dialog.bypass_errors = 1;
|
|
Ship_editor_dialog.update_data(0);
|
|
total = Wings[wing_num].wave_count;
|
|
for (i=0; i<total; i++) {
|
|
ptr = &Objects[wing_objects[wing_num][i]];
|
|
if (ptr->type == OBJ_SHIP)
|
|
remove_ship_from_wing(ptr->instance, 0);
|
|
else if (ptr->type == OBJ_START)
|
|
remove_player_from_wing(ptr->instance, 0);
|
|
}
|
|
|
|
Assert(!Wings[wing_num].wave_count);
|
|
|
|
Wings[wing_num].wave_count = 0;
|
|
Wings[wing_num].wing_squad_filename[0] = '\0';
|
|
Wings[wing_num].wing_insignia_texture = -1;
|
|
|
|
Ship_editor_dialog.initialize_data(1);
|
|
Ship_editor_dialog.bypass_errors = Wing_editor_dialog.bypass_errors = 0;
|
|
|
|
if (cur_wing == wing_num) {
|
|
set_cur_wing(cur_wing = -1); // yes, one '=' is correct.
|
|
}
|
|
|
|
free_sexp2(Wings[wing_num].arrival_cue);
|
|
free_sexp2(Wings[wing_num].departure_cue);
|
|
|
|
Num_wings--;
|
|
|
|
update_custom_wing_indexes();
|
|
|
|
set_modified();
|
|
}
|