Files
fs2open.github.com/code/lab/lab.cpp
T
Goober5000 3b2af702f7 refactor display_name for ships, ship classes, and weapon classes
Several things going on here:
1) refactor `get_display_string()` to `get_display_name()` to be more precise
2) likewise, refactor `alt_name` to `display_name`
3) automatically generate display names when parsing the ship, ship class, and weapon class for the first time
4) remove the on-the-fly truncation of names with hash symbols

Basically this subsumes the `end_string_at_first_hash_symbol` logic into the display name.  Rather than truncate the name every time it's displayed, truncate it once when it's parsed.  This takes advantage of the previous display name infrastructure, since display names are a superset of the case of truncating the hash symbol.

This also has the benefit of now allowing hash symbols within display names.  When updating Destiny of Peace, I noticed that "Lockheed Shuttle #4" would no longer display properly; and it was not previously possible to fix this even with display names.

I could pull out the `get_display_string()` and `alt_name` renaming into a separate PR if desired.
2020-08-21 00:15:38 -04:00

3135 lines
98 KiB
C++

/*
* lab.cpp
* created by WMCoolmon
*
* You may not sell or otherwise commercially exploit the source or things you
* create based on the source.
*
*/
#include "freespace.h"
#include "cmdline/cmdline.h"
#include "gamesequence/gamesequence.h"
#include "graphics/light.h"
#include "graphics/matrix.h"
#include "graphics/shadows.h"
#include "hud/hudshield.h"
#include "io/key.h"
#include "io/timer.h"
#include "lab/wmcgui.h"
#include "lighting/lighting.h"
#include "mission/missionparse.h"
#include "missionui/missionscreencommon.h"
#include "model/model.h"
#include "model/modelrender.h"
#include "nebula/neb.h"
#include "object/objectsnd.h"
#include "playerman/managepilot.h"
#include "particle/particle.h"
#include "render/3d.h"
#include "render/batching.h"
#include "ship/ship.h"
#include "ship/shiphit.h"
#include "ship/shipfx.h"
#include "debris/debris.h"
#include "species_defs/species_defs.h"
#include "starfield/nebula.h"
#include "starfield/starfield.h"
#include "tracing/tracing.h"
#include "weapon/beam.h"
#include "weapon/weapon.h"
#include <utility>
#include <weapon/muzzleflash.h>
#include <ai/aiinternal.h>
// flags
#define LAB_FLAG_NORMAL (0) // default
#define LAB_FLAG_NO_ROTATION (1 << 0) // don't rotate models
#define LAB_FLAG_SHOW_INSIGNIA (1 << 1) // show ships with insignia applied
#define LAB_FLAG_SHOW_DEBRIS (1 << 2) // render debris instead of normal LOD
#define LAB_FLAG_SUBMODEL_ROTATE (1 << 3) // do rotation for any rotating ship subobjects
#define LAB_FLAG_LIGHTNING_ARCS (1 << 4) // show damage lightning
#define LAB_FLAG_UNUSED (1 << 5) // feel free to change that to something you need - Valathil
#define LAB_FLAG_SHOW_WEAPONS (1 << 6) // determines if external weapons models are displayed
#define LAB_FLAG_INITIAL_ROTATION (1 << 7) // initial rotation setting
#define LAB_FLAG_DESTROYED_SUBSYSTEMS (1 << 8) // render model as if all subsystems are destroyed
#define LAB_FLAG_SHIP_EXPLODING (1 << 9) // execute the ship kill stuff
// modes
#define LAB_MODE_NONE 0 // not showing anything
#define LAB_MODE_SHIP 1 // dealing with ships
#define LAB_MODE_WEAPON 2 // dealing with weapons
// variables
static GUIScreen* Lab_screen = nullptr;
static Window* Lab_toolbar = nullptr;
static Window* Lab_class_window = nullptr;
static Window* Lab_class_toolbar = nullptr;
static Window* Lab_flags_window = nullptr;
static Window* Lab_render_options_window = nullptr;
static Window* Lab_material_override_window = nullptr;
static Window* Lab_variables_window = nullptr;
static Window* Lab_description_window = nullptr;
static Window* Lab_background_window = nullptr;
static Window* Lab_actions_window = nullptr;
static Window* Lab_subsystems_window = nullptr;
static Window* Lab_loadout_window = nullptr;
static Window* Lab_weapon_fire_window = nullptr;
static Window* Lab_trigger_animations_window = nullptr;
static Text* Lab_description_text = nullptr;
static TreeItem** Lab_species_nodes = nullptr;
static Tree* ship_tree = nullptr;
static Tree* weap_tree = nullptr;
// these are here unfortunately because otherwise there's no way to reset them
static Slider* ambient_sldr = nullptr;
static Slider* direct_sldr = nullptr;
static Slider* bloom_sldr = nullptr;
// holds the beginning and ending indices of each specie/type of ship/weapon
static SCP_vector<std::pair<TreeItem*, TreeItem*>> ship_list_endpoints;
static SCP_vector<std::pair<TreeItem*, TreeItem*>> weap_list_endpoints;
static TreeItem* Lab_last_selected_object = nullptr;
static int Lab_insignia_bitmap = -1;
static int Lab_insignia_index = -1;
static ubyte Lab_mode = LAB_MODE_NONE;
static int Lab_selected_index = -1;
static int Lab_last_selected_ship = -1;
static int Lab_selected_object = -1;
static int Lab_last_selected_weapon = -1;
static int Lab_model_num = -1;
static int Lab_model_LOD = 0;
static char Lab_model_filename[MAX_FILENAME_LEN];
static vec3d Lab_model_pos = ZERO_VECTOR;
static matrix Lab_model_orient = vmd_identity_matrix;
static int Lab_viewer_flags = LAB_MODE_NONE;
static int Lab_detail_texture_save = 0;
SCP_string Lab_selected_mission = "None";
matrix Lab_skybox_orientation = vmd_identity_matrix;
static int anim_timer_start = 0;
static float Lab_thrust_len = 1.0f;
static bool Lab_thrust_afterburn = false;
enum class Lab_rotation_modes { Both, Yaw, Pitch, Roll };
static Lab_rotation_modes Lab_rotation_mode = Lab_rotation_modes::Both;
static float Lab_manual_rotation_speed_divisor = 100.f;
// Trackball_mode:
// 1 == rotate (left-mouse)
// 2 == pan (shift-left-mouse)
// 3 == zoom (right-mouse)
static int Trackball_mode = 1;
static int Trackball_active = 0;
SCP_string Lab_team_color = "<none>";
//save the original cmdline values in case we need to reset to them
int orig_cmdline_ambient;
float orig_cmdline_direct;
int orig_cmdline_bloom;
camid Lab_cam;
float lab_cam_distance = 100.0f;
float lab_cam_phi = 0;
float lab_cam_theta = 0;
bool Lab_render_wireframe = false;
bool Lab_render_without_light = false;
bool Lab_render_show_thrusters = false;
bool Lab_render_show_detail = false;
bool Lab_render_show_shields = false;
bool Lab_render_show_weapons = false;
bool Lab_rotate_subobjects = true;
bool Lab_Basemap_override = false;
bool Lab_Glowmap_override = false;
bool Lab_Specmap_override = false;
bool Lab_Envmap_override = false;
bool Lab_Normalmap_override = false;
bool Lab_Heightmap_override = false;
bool Lab_Miscmap_override = false;
bool Lab_emissive_light_override = (Cmdline_emissive != 0);
fix Lab_Save_Missiontime;
int Lab_fire_primaries = 0;
int Lab_fire_secondaries = 0;
bool Lab_weapons_loaded = false;
// functions
void labviewer_change_ship_lod(Tree* caller);
void labviewer_change_ship(Tree* caller);
void labviewer_make_desc_window(Button* caller);
void labviewer_update_flags_window();
void labviewer_update_actions_window();
void labviewer_load_weapons();
// ---------------------- General/Utility Functions ----------------------------
void rotate_view(int dx, int dy)
{
if (dx == 0 && dy == 0)
return;
lab_cam_theta += dx / 100.0f;
lab_cam_phi += dy / 100.0f;
CLAMP(lab_cam_phi, 0.01f, PI - 0.01f);
}
void reset_view()
{
//reset position
Lab_model_pos = vmd_zero_vector;
Lab_model_orient = vmd_identity_matrix;
//reset lighting
//if the rendering options window is close, these sliders are null
//we still want to be able to reset lighting in this situation though, so just set the values directly
if (ambient_sldr == nullptr) {
gr_calculate_ambient_factor(orig_cmdline_ambient);
static_light_factor = orig_cmdline_direct;
Cmdline_bloom_intensity = orig_cmdline_bloom;
} else {
ambient_sldr->SetSliderValue((float)orig_cmdline_ambient);
direct_sldr->SetSliderValue(orig_cmdline_direct);
bloom_sldr->SetSliderValue((float)orig_cmdline_bloom);
}
}
void labviewer_change_model(char* model_fname, int lod = 0, int sel_index = -1)
{
bool change_model = true;
anim_timer_start = timer_get_milliseconds();
if ((model_fname != nullptr) && (Lab_mode == LAB_MODE_SHIP)) {
if ((Lab_selected_index >= 0) && (Lab_selected_index == sel_index)) {
change_model = false;
}
}
Lab_selected_index = sel_index;
if (change_model) {
bool valid_ship = ((Lab_mode == LAB_MODE_SHIP) && (sel_index >= 0));
if (Lab_model_num != -1) {
model_page_out_textures(Lab_model_num, true);
model_unload(Lab_model_num);
Lab_model_num = -1;
if (Lab_last_selected_ship >= 0) {
ship_page_out_textures(Lab_last_selected_ship, true);
}
}
// only load a new model if we are supposed to (so that we can use this function to reset states)
if (model_fname != nullptr) {
model_subsystem* subsystems = nullptr;
Lab_model_num =
model_load(model_fname, (valid_ship) ? Ship_info[sel_index].n_subsystems : 0, subsystems, 0);
if (valid_ship) {
if (Lab_model_num >= 0) {
model_page_in_textures(Lab_model_num, sel_index);
}
}
} else {
// clear out the model filename
memset(Lab_model_filename, 0, sizeof(Lab_model_filename));
}
}
if (lod == 99) {
Lab_model_LOD = 0;
Lab_viewer_flags |= LAB_FLAG_SHOW_DEBRIS;
} else {
Lab_model_LOD = lod;
Lab_viewer_flags &= ~LAB_FLAG_SHOW_DEBRIS;
}
if (model_fname == nullptr) {
Lab_last_selected_ship = -1;
}
labviewer_update_flags_window();
labviewer_update_actions_window();
}
void labviewer_recalc_camera()
{
auto cam = Lab_cam.getCamera();
if (Lab_selected_object != -1) {
vec3d new_position;
new_position.xyz.x = sinf(lab_cam_phi) * cosf(lab_cam_theta);
new_position.xyz.y = cosf(lab_cam_phi);
new_position.xyz.z = sinf(lab_cam_phi) * sinf(lab_cam_theta);
vm_vec_scale(&new_position, lab_cam_distance);
cam->set_position(&new_position);
cam->set_rotation_facing(&Lab_model_pos);
}
}
void labviewer_render_model(float frametime)
{
angles rot_angles;
float rev_rate;
ship_info* sip = nullptr;
if (Lab_selected_object != -1 && (Objects[Lab_selected_object].type == OBJ_SHIP) && (Lab_selected_index >= 0)) {
sip = &Ship_info[Lab_selected_index];
}
light_reset();
// get correct revolution rate
rev_rate = REVOLUTION_RATE;
if (sip != nullptr) {
if (sip->is_big_ship()) {
rev_rate *= 1.7f;
} else if (sip->is_huge_ship()) {
rev_rate *= 3.0f;
}
}
// rotate/pan/zoom the model as much as required for this frame
if (Trackball_active) {
int dx, dy;
mouse_get_delta(&dx, &dy);
if (dx || dy) {
// Rotate the ship
if (Trackball_mode == 1 && !(Lab_viewer_flags & LAB_FLAG_SHIP_EXPLODING)) {
angles rot_angle;
vm_extract_angles_matrix_alternate(&rot_angle, &Lab_model_orient);
if (Lab_rotation_mode == Lab_rotation_modes::Yaw) {
rot_angle.h += dx / Lab_manual_rotation_speed_divisor;
}
if (Lab_rotation_mode == Lab_rotation_modes::Pitch) {
rot_angle.p += dy / Lab_manual_rotation_speed_divisor;
}
if (Lab_rotation_mode == Lab_rotation_modes::Both) {
rot_angle.h += dx / Lab_manual_rotation_speed_divisor;
rot_angle.p += dy / Lab_manual_rotation_speed_divisor;
}
if (Lab_rotation_mode == Lab_rotation_modes::Roll) {
rot_angle.b += dx / Lab_manual_rotation_speed_divisor;
}
if (rot_angle.h < -PI)
rot_angle.h = PI - 0.001f;
if (rot_angle.h > PI)
rot_angle.h = -PI + 0.001f;
CLAMP(rot_angle.p, -PI_2, PI_2);
if (rot_angle.b < -PI)
rot_angle.b = PI - 0.001f;
if (rot_angle.b > PI)
rot_angle.b = -PI + 0.001f;
vm_angles_2_matrix(&Lab_model_orient, &rot_angle);
}
// zoom in/out
else if (Trackball_mode == 2) {
lab_cam_distance *= 1.0f + (dy / 20.0f);
if (lab_cam_distance < 1.0f)
lab_cam_distance = 1.0f;
}
// rotate background
else if (Trackball_mode == 3) {
rotate_view(dx, dy);
}
labviewer_recalc_camera();
}
}
// otherwise do orient/rotation calculation, if we are supposed to
else if (!(Lab_viewer_flags & LAB_FLAG_NO_ROTATION)) {
rot_angles.p = 0.0f;
rot_angles.b = 0.0f;
rot_angles.h = PI2 * frametime / rev_rate;
vm_rotate_matrix_by_angles(&Lab_model_orient, &rot_angles);
}
if (Lab_selected_object != -1)
{
auto lab_render_light_save = Lab_render_without_light;
auto lab_debris_override_save = Motion_debris_enabled;
auto lab_envmap_override_save = Envmap_override;
auto lab_emissive_light_save = Cmdline_emissive;
if (Lab_selected_mission == "None") {
Lab_render_without_light = true;
Motion_debris_enabled = true;
}
Cmdline_emissive = Lab_emissive_light_override;
object* obj = &Objects[Lab_selected_object];
obj->pos = Lab_model_pos;
obj->orient = Lab_model_orient;
Envmap_override = Lab_Envmap_override;
if (obj->type == OBJ_SHIP) {
Ships[obj->instance].flags.set(Ship::Ship_Flags::Rotators_locked, !Lab_rotate_subobjects);
Ships[obj->instance].flags.set(Ship::Ship_Flags::Draw_as_wireframe, Lab_render_wireframe);
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_full_detail, Lab_render_show_detail);
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_light, Lab_render_without_light);
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_diffuse, Lab_Basemap_override);
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_glowmap, Lab_Glowmap_override);
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_normalmap, Lab_Normalmap_override);
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_specmap, Lab_Specmap_override);
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_heightmap, Lab_Heightmap_override);
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_miscmap, Lab_Miscmap_override);
Ships[obj->instance].flags.set(Ship::Ship_Flags::Render_without_weapons, !Lab_render_show_weapons);
if (Lab_render_show_weapons && !Lab_weapons_loaded)
labviewer_load_weapons();
ship_model_update_instance(obj);
Ships[obj->instance].team_name = Lab_team_color;
if (Lab_viewer_flags & LAB_FLAG_LIGHTNING_ARCS) {
obj->hull_strength = 1.0f;
}
else {
obj->hull_strength = Ship_info[Ships[obj->instance].ship_info_index].max_hull_strength;
}
bool weapons_firing = false;
for (auto i = 0; i < Ships[obj->instance].weapons.num_primary_banks; ++i) {
if (Lab_fire_primaries & (1 << i)) {
weapons_firing = true;
Ships[obj->instance].weapons.current_primary_bank = i;
ship_fire_primary(obj, 0);
}
}
Ships[obj->instance].flags.set(Ship::Ship_Flags::Trigger_down, weapons_firing);
for (auto i = 0; i < Ships[obj->instance].weapons.num_secondary_banks; ++i) {
if (Lab_fire_secondaries & (1 << i)) {
Ships[obj->instance].weapons.current_secondary_bank = i;
ship_fire_secondary(obj);
}
}
}
if (Lab_render_wireframe)
model_render_set_wireframe_color(&Color_white);
if (Lab_render_show_thrusters) {
obj->phys_info.forward_thrust = 1.0f;
if (obj->type == OBJ_SHIP) {
Ships[obj->instance].flags.remove(Ship::Ship_Flags::No_thrusters);
}
if (Lab_thrust_afterburn)
obj->phys_info.flags |= PF_AFTERBURNER_ON;
else
obj->phys_info.flags &= ~PF_AFTERBURNER_ON;
}
else {
obj->phys_info.forward_thrust = 0.0f;
if (obj->type == OBJ_SHIP)
Ships[obj->instance].flags.set(Ship::Ship_Flags::No_thrusters);
}
obj_move_all(frametime);
process_subobjects(Lab_selected_object);
particle::move_all(frametime);
particle::ParticleManager::get()->doFrame(frametime);
shockwave_move_all(frametime);
Trail_render_override = true;
game_render_frame(Lab_cam);
Trail_render_override = false;
Lab_render_without_light = lab_render_light_save;
Motion_debris_enabled = lab_debris_override_save;
Envmap_override = lab_envmap_override_save;
Cmdline_emissive = lab_emissive_light_save;
gr_reset_clip();
gr_set_color_fast(&HUD_color_debug);
if (Cmdline_frame_profile) {
tracing::frame_profile_process_frame();
gr_string(gr_screen.center_offset_x + 20, gr_screen.center_offset_y + 100 + gr_get_font_height() + 1,
tracing::get_frame_profile_output().c_str(), GR_RESIZE_NONE);
}
}
}
SCP_string get_rot_mode_string(Lab_rotation_modes rotmode)
{
switch (rotmode) {
case Lab_rotation_modes::Both:
return "Manual rotation mode: Pitch and Yaw";
case Lab_rotation_modes::Pitch:
return "Manual rotation mode: Pitch";
case Lab_rotation_modes::Yaw:
return "Manual rotation mode: Yaw";
case Lab_rotation_modes::Roll:
return "Manual rotation mode: Roll";
default:
return "HOW DID THIS HAPPEN? Ask a coder!";
}
}
SCP_string get_rot_speed_string(float speed_divisor)
{
auto exp = std::lroundf(log10f(speed_divisor));
switch (exp) {
case 2:
return "Fast";
case 3:
return "Slow";
case 4:
return "Slowest";
default:
return "HOW DID THIS HAPPEN? Ask a coder!";
}
}
void labviewer_do_render(float frametime)
{
GR_DEBUG_SCOPE("Lab Render");
int w, h;
// render our particular thing
if (Lab_selected_object >= 0) {
labviewer_render_model(frametime);
// print out the current pof filename, to help with... something
if (strlen(Lab_model_filename)) {
gr_get_string_size(&w, &h, Lab_model_filename);
gr_set_color_fast(&Color_white);
gr_string(gr_screen.center_offset_x + gr_screen.center_w - w,
gr_screen.center_offset_y + gr_screen.center_h - h, Lab_model_filename, GR_RESIZE_NONE);
}
}
// print FPS at bottom left, might be helpful
extern void game_get_framerate();
extern float frametotal;
extern float Framerate;
game_get_framerate();
gr_set_color_fast(&Color_white);
if (frametotal != 0.0f) {
gr_printf_no_resize(gr_screen.center_offset_x + 2,
gr_screen.center_offset_y + gr_screen.center_h - gr_get_font_height(),
"FPS: %3i Camera Distance: %4f", (int)std::lround(Framerate), lab_cam_distance);
} else {
gr_string(gr_screen.center_offset_x + 10, gr_screen.center_offset_y + gr_screen.center_h - gr_get_font_height(),
"FPS: ?", GR_RESIZE_NONE);
}
//Print FXAA preset
if ((gr_is_fxaa_mode(Gr_aa_mode) || gr_is_smaa_mode(Gr_aa_mode)) && !PostProcessing_override) {
const char* aa_mode;
switch (Gr_aa_mode) {
case AntiAliasMode::FXAA_Low:
aa_mode = "FXAA Low";
break;
case AntiAliasMode::FXAA_Medium:
aa_mode = "FXAA Medium";
break;
case AntiAliasMode::FXAA_High:
aa_mode = "FXAA High";
break;
case AntiAliasMode::SMAA_Low:
aa_mode = "SMAA Low";
break;
case AntiAliasMode::SMAA_Medium:
aa_mode = "SMAA Medium";
break;
case AntiAliasMode::SMAA_High:
aa_mode = "SMAA High";
break;
case AntiAliasMode::SMAA_Ultra:
aa_mode = "SMAA Ultra";
break;
default:
aa_mode = "None";
break;
}
gr_printf_no_resize(gr_screen.center_offset_x + 2, gr_screen.center_offset_y + gr_screen.center_h - (gr_get_font_height() * 2) - 3, "AA Preset: %s", aa_mode);
}
//Print current Team Color setting, if any
if (Lab_team_color != "<none>")
gr_printf_no_resize(gr_screen.center_offset_x + 2,
gr_screen.center_offset_y + gr_screen.center_h - (gr_get_font_height() * 3) - 3,
"Use T and Y to cycle through available Team Color settings. Current: %s",
Lab_team_color.c_str());
// Camera usage info
gr_printf_no_resize(gr_screen.center_offset_x + 2,
gr_screen.center_offset_y + gr_screen.center_h - (gr_get_font_height() * 4) - 3,
"Hold LMB to rotate the ship or weapon. Hold RMB to rotate the Camera. Hold Shift + LMB to "
"zoom in or out. Use number keys to switch between AA presets. R to cycle model rotation "
"modes, S to cycle model rotation speeds, V to reset view.");
// Rotation mode
angles rot_angle;
vm_extract_angles_matrix(&rot_angle, &Lab_model_orient);
SCP_string text = get_rot_mode_string(Lab_rotation_mode);
gr_printf_no_resize(gr_screen.center_offset_x + 2,
gr_screen.center_offset_y + gr_screen.center_h - (gr_get_font_height() * 5) - 3,
"%s Rotation speed: %s", get_rot_mode_string(Lab_rotation_mode).c_str(),
get_rot_speed_string(Lab_manual_rotation_speed_divisor).c_str());
}
void labviewer_exit(Button* /*caller*/)
{
if (Lab_selected_object != -1) {
obj_delete(Lab_selected_object);
}
gameseq_post_event(GS_EVENT_PREVIOUS_STATE);
}
// ---------------------------- Class Window ----------------------------------
void labviewer_close_class_window(GUIObject* /*caller*/)
{
if (Lab_class_toolbar) {
Lab_class_toolbar->DeleteChildren();
}
Lab_class_window = nullptr;
Lab_mode = LAB_MODE_NONE;
// reset any existing model/bitmap that is showing
labviewer_change_model(nullptr);
}
void labviewer_set_class_window(int mode)
{
if (Lab_class_window == nullptr) {
Lab_class_window = (Window*)Lab_screen->Add(
new Window("Class Window", gr_screen.center_offset_x + 50, gr_screen.center_offset_y + 50));
Lab_class_window->SetCloseFunction(labviewer_close_class_window);
}
if (Lab_class_toolbar == nullptr) {
Lab_class_toolbar = (Window*)Lab_screen->Add(new Window("Class Toolbar", gr_screen.center_offset_x + 0,
gr_screen.center_offset_y + Lab_toolbar->GetHeight(),
-1, -1, WS_NOTITLEBAR | WS_NONMOVEABLE));
}
// clear out all existing children
Lab_class_window->ClearContent();
Lab_class_toolbar->ClearContent();
// set our new title
if (mode == LAB_MODE_SHIP) {
Lab_class_window->SetCaption("Ship Classes");
} else if (mode == LAB_MODE_WEAPON) {
Lab_class_window->SetCaption("Weapon Classes");
}
// reset any existing model/bitmap that is showing
labviewer_change_model(nullptr);
}
// ------------------------------ Flags Window --------------------------------
template <class T>
struct lab_flag {
Checkbox* cb;
T flag;
};
static SCP_vector<lab_flag<Ship::Info_Flags>> Ship_Class_Flags;
static SCP_vector<lab_flag<Weapon::Info_Flags>> Weapon_Class_Flags;
void labviewer_flags_clear()
{
if (Lab_flags_window != nullptr) {
Lab_flags_window->DeleteChildren();
}
Ship_Class_Flags.clear();
Weapon_Class_Flags.clear();
}
template <class T>
void labviewer_flags_add(int* X, int* Y, const char* flag_name, T flag, SCP_vector<lab_flag<T>>& flag_list)
{
int x = 0, y = 0;
Assert((Lab_flags_window != nullptr) && (flag_name != nullptr));
lab_flag<T> new_flag;
if (X) {
x = *X;
}
if (Y) {
y = *Y;
}
new_flag.cb = (Checkbox*)Lab_flags_window->AddChild(new Checkbox(flag_name, x, y));
new_flag.flag = flag;
flag_list.push_back(new_flag);
if (X) {
*X += new_flag.cb->GetWidth() + 2;
}
if (Y) {
*Y += new_flag.cb->GetHeight() + 1;
}
}
void labviewer_populate_flags_window()
{
if (Lab_mode == LAB_MODE_NONE) {
return;
}
if (Lab_flags_window == nullptr) {
return;
}
// clear out anything that already exists
labviewer_flags_clear();
int y = 0;
// ship flags ...
if (Lab_mode == LAB_MODE_SHIP) {
for (size_t i = 0; i < Num_ship_flags; ++i) {
labviewer_flags_add<Ship::Info_Flags>(nullptr, &y, Ship_flags[i].name, Ship_flags[i].def, Ship_Class_Flags);
}
}
// weapon flags ...
else if (Lab_mode == LAB_MODE_WEAPON) {
for (size_t i = 0; i < num_weapon_info_flags; ++i) {
labviewer_flags_add<Weapon::Info_Flags>(nullptr, &y, Weapon_Info_Flags[i].name, Weapon_Info_Flags[i].def,
Weapon_Class_Flags);
}
}
}
void labviewer_update_flags_window()
{
if ((Lab_selected_index < 0) || (Lab_mode == LAB_MODE_NONE)) {
return;
}
if (Lab_mode == LAB_MODE_SHIP) {
auto sip = &Ship_info[Lab_selected_index];
for (auto flag_def : Ship_Class_Flags) {
if (flag_def.flag == Ship::Info_Flags::NUM_VALUES)
continue;
flag_def.cb->SetFlag(sip->flags, flag_def.flag, sip);
}
} else if (Lab_mode == LAB_MODE_WEAPON) {
auto wip = &Weapon_info[Lab_selected_index];
for (auto flag_def : Weapon_Class_Flags) {
if (flag_def.flag == Weapon::Info_Flags::NUM_VALUES)
continue;
flag_def.cb->SetFlag(wip->wi_flags, flag_def.flag, wip);
}
}
}
void labviewer_close_flags_window(GUIObject* /*caller*/)
{
Lab_flags_window = nullptr;
Ship_Class_Flags.clear();
Weapon_Class_Flags.clear();
}
void labviewer_make_flags_window(Button* /*caller*/)
{
if (Lab_flags_window == nullptr) {
Lab_flags_window = (Window*)Lab_screen->Add(new Window(
"Flags Window", gr_screen.center_offset_x + gr_screen.center_w - 205, gr_screen.center_offset_y + 200));
Lab_flags_window->SetCloseFunction(labviewer_close_flags_window);
}
// set our new title
if (Lab_mode == LAB_MODE_SHIP) {
Lab_flags_window->SetCaption("Ship Flags");
} else if (Lab_mode == LAB_MODE_WEAPON) {
Lab_flags_window->SetCaption("Weapon Flags");
}
// populate the window with our flags (controls both ships and weapons flags)
labviewer_populate_flags_window();
// update content, if we need to
labviewer_update_flags_window();
}
// ----------------------- Variables Window --------------------------------
#define VAR_POS_LEFTWIDTH 150
#define VAR_POS_RIGHTWIDTH 100
#define VAR_POS_RIGHTX 160
#define VAR_ADD_HEADER(name) \
{ \
ntp = (Text*)Lab_variables_window->AddChild( \
new Text((name), (name), VAR_POS_RIGHTX / 2, y + 8, VAR_POS_RIGHTWIDTH)); \
y += ntp->GetHeight() + 10; \
}
static SCP_vector<Text*> Lab_variables;
void labviewer_close_variables_window(GUIObject* /*caller*/)
{
Lab_variables_window = nullptr;
Lab_variables.clear();
}
void labviewer_variables_clear()
{
if (Lab_variables_window != nullptr) {
Lab_variables_window->DeleteChildren();
}
Lab_variables.clear();
}
void labviewer_variables_add(int* Y, const char* var_name)
{
int y = 0;
Text* new_text;
Assert((Lab_variables_window != nullptr) && (var_name != nullptr));
if (Y) {
y = *Y;
}
// variable
Lab_variables_window->AddChild(new Text((var_name), (var_name), 0, y, VAR_POS_LEFTWIDTH));
// edit box
new_text = (Text*)Lab_variables_window->AddChild(new Text(SCP_string((var_name)) + SCP_string("Editbox"), "",
VAR_POS_RIGHTX, y, VAR_POS_RIGHTWIDTH, gr_get_font_height() + 2, T_EDITTABLE));
if (Y) {
*Y += new_text->GetHeight() + 2;
}
Lab_variables.push_back(new_text);
}
void labviewer_populate_variables_window()
{
Text* ntp;
int y;
if (Lab_mode == LAB_MODE_NONE) {
return;
}
if (Lab_variables_window == nullptr) {
return;
}
// clear out anything that already exists
labviewer_variables_clear();
y = 0;
// IMPORTANT NOTE: If you add something here, make sure you add it to labviewer_update_variables_window() as well!
// ship vFEWfe<ariables ...
if (Lab_mode == LAB_MODE_SHIP) {
labviewer_variables_add(&y, "Name");
labviewer_variables_add(&y, "Species");
labviewer_variables_add(&y, "Type");
labviewer_variables_add(&y, "Default Team Color");
// physics
VAR_ADD_HEADER("Physics");
labviewer_variables_add(&y, "Density");
labviewer_variables_add(&y, "Damp");
labviewer_variables_add(&y, "Rotdamp");
labviewer_variables_add(&y, "Max vel (x)");
labviewer_variables_add(&y, "Max vel (y)");
labviewer_variables_add(&y, "Max vel (z)");
labviewer_variables_add(&y, "Warp in speed");
labviewer_variables_add(&y, "Warp out speed");
// other
VAR_ADD_HEADER("Stats");
labviewer_variables_add(&y, "Shields");
labviewer_variables_add(&y, "Hull");
labviewer_variables_add(&y, "Subsys repair rate");
labviewer_variables_add(&y, "Hull repair rate");
labviewer_variables_add(&y, "Countermeasures");
labviewer_variables_add(&y, "HUD Icon");
VAR_ADD_HEADER("Power");
labviewer_variables_add(&y, "Power output");
labviewer_variables_add(&y, "Max oclk speed");
labviewer_variables_add(&y, "Max weapon reserve");
VAR_ADD_HEADER("Afterburner");
labviewer_variables_add(&y, "Fuel");
labviewer_variables_add(&y, "Burn rate");
labviewer_variables_add(&y, "Recharge rate");
VAR_ADD_HEADER("Explosion");
labviewer_variables_add(&y, "Inner radius");
labviewer_variables_add(&y, "Outer radius");
labviewer_variables_add(&y, "Damage");
labviewer_variables_add(&y, "Blast");
labviewer_variables_add(&y, "Propagates");
labviewer_variables_add(&y, "Shockwave speed");
labviewer_variables_add(&y, "Shockwave count");
// techroom
VAR_ADD_HEADER("Techroom");
labviewer_variables_add(&y, "Closeup zoom");
labviewer_variables_add(&y, "Closeup pos (x)");
labviewer_variables_add(&y, "Closeup pos (y)");
labviewer_variables_add(&y, "Closeup pos (z)");
}
// weapon variables ...
else if (Lab_mode == LAB_MODE_WEAPON) {
labviewer_variables_add(&y, "Name");
labviewer_variables_add(&y, "Subtype");
// physics
VAR_ADD_HEADER("Physics");
labviewer_variables_add(&y, "Mass");
labviewer_variables_add(&y, "Max speed");
labviewer_variables_add(&y, "Lifetime");
labviewer_variables_add(&y, "Range");
labviewer_variables_add(&y, "Min Range");
VAR_ADD_HEADER("Damage");
labviewer_variables_add(&y, "Fire wait");
labviewer_variables_add(&y, "Damage");
labviewer_variables_add(&y, "Armor factor");
labviewer_variables_add(&y, "Shield factor");
labviewer_variables_add(&y, "Subsys factor");
VAR_ADD_HEADER("Armor");
labviewer_variables_add(&y, "Damage type");
VAR_ADD_HEADER("Shockwave");
labviewer_variables_add(&y, "Speed");
VAR_ADD_HEADER("Missiles");
labviewer_variables_add(&y, "Turn time");
labviewer_variables_add(&y, "FOV");
labviewer_variables_add(&y, "Min locktime");
labviewer_variables_add(&y, "Pixels/sec");
labviewer_variables_add(&y, "Catchup pixels/sec");
labviewer_variables_add(&y, "Catchup pixel pen.");
labviewer_variables_add(&y, "Swarm count");
labviewer_variables_add(&y, "Swarm wait");
}
}
#define VAR_SET_VALUE(value) \
{ \
Assert(i < Lab_variables.size()); \
Lab_variables[i]->SetText((value)); \
i++; \
}
#define VAR_SET_VALUE_SAVE(value, max_size) \
{ \
Assert(i < Lab_variables.size()); \
Lab_variables[i]->SetText((value)); \
if ((max_size) < 1) { \
Assert((max_size) == 0); \
Lab_variables[i]->SetSaveLoc(&(value), T_ST_ONENTER); \
} else { \
Lab_variables[i]->SetSaveLoc(&(value), T_ST_ONENTER, (max_size), 0); \
} \
i++; \
}
extern SCP_vector<SCP_string> Hud_shield_filenames;
void labviewer_update_variables_window()
{
uint i = 0;
if (Lab_mode == LAB_MODE_NONE) {
return;
}
if (Lab_variables_window == nullptr) {
return;
}
if (Lab_selected_index < 0) {
return;
}
// IMPORTANT NOTE: If you add something here, make sure you add it to labviewer_populate_variables_window() as well!
// ship variables ...
if (Lab_mode == LAB_MODE_SHIP) {
Assert(Lab_selected_index < ship_info_size());
ship_info* sip = &Ship_info[Lab_selected_index];
VAR_SET_VALUE(sip->name);
VAR_SET_VALUE_SAVE(sip->species, (int)(Species_info.size() - 1));
VAR_SET_VALUE_SAVE(sip->class_type, (int)(Ship_types.size() - 1));
VAR_SET_VALUE(sip->default_team_name);
VAR_SET_VALUE_SAVE(sip->density, 0);
VAR_SET_VALUE_SAVE(sip->damp, 0);
VAR_SET_VALUE_SAVE(sip->rotdamp, 0);
VAR_SET_VALUE_SAVE(sip->max_vel.xyz.x, 0);
VAR_SET_VALUE_SAVE(sip->max_vel.xyz.y, 0);
VAR_SET_VALUE_SAVE(sip->max_vel.xyz.z, 0);
VAR_SET_VALUE_SAVE(sip->max_shield_strength, 0);
VAR_SET_VALUE_SAVE(sip->max_hull_strength, 0);
VAR_SET_VALUE_SAVE(sip->subsys_repair_rate, 0);
VAR_SET_VALUE_SAVE(sip->hull_repair_rate, 0);
VAR_SET_VALUE_SAVE(sip->cmeasure_max, 0);
VAR_SET_VALUE_SAVE(sip->shield_icon_index, (int)(Hud_shield_filenames.size() - 1));
VAR_SET_VALUE_SAVE(sip->power_output, 0);
VAR_SET_VALUE_SAVE(sip->max_overclocked_speed, 0);
VAR_SET_VALUE_SAVE(sip->max_weapon_reserve, 0);
// VAR_SET_VALUE_SAVE(sip->max_shield_regen_per_second, 0);
// VAR_SET_VALUE_SAVE(sip->max_weapon_regen_per_second, 0);
VAR_SET_VALUE_SAVE(sip->afterburner_fuel_capacity, 0);
VAR_SET_VALUE_SAVE(sip->afterburner_burn_rate, 0);
VAR_SET_VALUE_SAVE(sip->afterburner_recover_rate, 0);
VAR_SET_VALUE_SAVE(sip->shockwave.inner_rad, 0);
VAR_SET_VALUE_SAVE(sip->shockwave.outer_rad, 0);
VAR_SET_VALUE_SAVE(sip->shockwave.damage, 0);
VAR_SET_VALUE_SAVE(sip->shockwave.blast, 0);
VAR_SET_VALUE_SAVE(sip->explosion_propagates, 0);
VAR_SET_VALUE_SAVE(sip->shockwave.speed, 0);
VAR_SET_VALUE_SAVE(sip->shockwave_count, 0);
VAR_SET_VALUE_SAVE(sip->closeup_zoom, 0);
VAR_SET_VALUE_SAVE(sip->closeup_pos.xyz.x, 0.0f);
VAR_SET_VALUE_SAVE(sip->closeup_pos.xyz.y, 0.0f);
VAR_SET_VALUE_SAVE(sip->closeup_pos.xyz.z, 0.0f);
}
// weapon variables ...
else if (Lab_mode == LAB_MODE_WEAPON) {
Assert(Lab_selected_index < weapon_info_size());
weapon_info* wip = &Weapon_info[Lab_selected_index];
VAR_SET_VALUE(wip->get_display_name());
VAR_SET_VALUE_SAVE(wip->subtype, Num_weapon_subtypes - 1);
VAR_SET_VALUE_SAVE(wip->mass, 0);
VAR_SET_VALUE_SAVE(wip->max_speed, 0);
VAR_SET_VALUE_SAVE(wip->lifetime, 0);
VAR_SET_VALUE_SAVE(wip->weapon_range, 0);
VAR_SET_VALUE_SAVE(wip->WeaponMinRange, 0);
VAR_SET_VALUE_SAVE(wip->fire_wait, 0);
VAR_SET_VALUE_SAVE(wip->damage, 0);
VAR_SET_VALUE_SAVE(wip->armor_factor, 0);
VAR_SET_VALUE_SAVE(wip->shield_factor, 0);
VAR_SET_VALUE_SAVE(wip->subsystem_factor, 0);
VAR_SET_VALUE_SAVE(wip->damage_type_idx, 0);
VAR_SET_VALUE_SAVE(wip->shockwave.speed, 0);
VAR_SET_VALUE_SAVE(wip->turn_time, 0);
VAR_SET_VALUE_SAVE(wip->fov, 0);
VAR_SET_VALUE_SAVE(wip->min_lock_time, 0);
VAR_SET_VALUE_SAVE(wip->lock_pixels_per_sec, 0);
VAR_SET_VALUE_SAVE(wip->catchup_pixels_per_sec, 0);
VAR_SET_VALUE_SAVE(wip->catchup_pixel_penalty, 0);
VAR_SET_VALUE_SAVE(wip->swarm_count, 0);
VAR_SET_VALUE_SAVE(wip->SwarmWait, 0);
}
}
void labviewer_make_variables_window(Button* /*caller*/)
{
if (Lab_variables_window != nullptr) {
return;
}
Lab_variables_window = (Window*)Lab_screen->Add(new Window(
"Class Variables", gr_screen.center_offset_x + gr_screen.center_w - (VAR_POS_RIGHTX + VAR_POS_RIGHTWIDTH + 25),
gr_screen.center_offset_y + 200));
// set our new title
if (Lab_mode == LAB_MODE_SHIP) {
Lab_variables_window->SetCaption("Ship Variables");
} else if (Lab_mode == LAB_MODE_WEAPON) {
Lab_variables_window->SetCaption("Weapon Variables");
}
// populate the window with our flags (controls both ships and weapons flags)
labviewer_populate_variables_window();
// update content, if we need to
labviewer_update_variables_window();
Lab_variables_window->SetCloseFunction(labviewer_close_variables_window);
}
// -------------------- Render Options Window ------------------------------
void labviewer_change_detail_texture(Tree* caller)
{
int slider_pos = (int)(caller->GetSelectedItem()->GetData());
Assert((slider_pos >= 0) && (slider_pos <= MAX_DETAIL_LEVEL));
Detail.hardware_textures = slider_pos;
}
void labviewer_close_render_options_window(GUIObject* /*caller*/)
{
ambient_sldr = nullptr;
bloom_sldr = nullptr;
direct_sldr = nullptr;
Lab_render_options_window = nullptr;
}
#define ADD_RENDER_FLAG(text, flag, var) \
{ \
cbp = (Checkbox*)Lab_render_options_window->AddChild(new Checkbox((text), 2, y)); \
cbp->SetFlag(&(flag), (var)); \
y += cbp->GetHeight() + 2; \
}
#define ADD_RENDER_BOOL(text, flag) \
{ \
cbp = (Checkbox*)Lab_render_options_window->AddChild(new Checkbox((text), 2, y)); \
cbp->SetBool(&(flag)); \
y += cbp->GetHeight() + 1; \
}
void labviewer_render_options_set_ambient_factor(Slider* caller)
{
gr_calculate_ambient_factor(fl2i(caller->GetSliderValue()));
}
void labviewer_render_options_set_static_light_factor(Slider* caller)
{
static_light_factor = caller->GetSliderValue();
}
void labviewer_render_options_set_bloom(Slider* caller) { Cmdline_bloom_intensity = fl2i(caller->GetSliderValue()); }
void labviewer_make_render_options_window(Button* /*caller*/)
{
Checkbox* cbp;
Tree* cmp;
TreeItem* ctip;
int y = 0;
if (Lab_render_options_window != nullptr) {
return;
}
Lab_render_options_window = (Window*)Lab_screen->Add(new Window(
"Render Options", gr_screen.center_offset_x + gr_screen.center_w - 300, gr_screen.center_offset_y + 200));
Assert(Lab_render_options_window != nullptr);
// add all of the flags that we want/need...
// viewer flags
ADD_RENDER_FLAG("Disable Model Rotation", Lab_viewer_flags, LAB_FLAG_NO_ROTATION);
ADD_RENDER_FLAG("Show Insignia", Lab_viewer_flags, LAB_FLAG_SHOW_INSIGNIA);
ADD_RENDER_FLAG("Show Damage Lightning", Lab_viewer_flags, LAB_FLAG_LIGHTNING_ARCS);
ADD_RENDER_BOOL("Rotate Subsystems", Lab_rotate_subobjects);
if (Gr_post_processing_enabled) {
ADD_RENDER_BOOL("Hide Post Processing", PostProcessing_override);
}
// map related flags
ADD_RENDER_BOOL("No Diffuse Map", Lab_Basemap_override);
if (Cmdline_glow) {
ADD_RENDER_BOOL("No Glow Map", Lab_Glowmap_override);
}
if (Cmdline_spec) {
ADD_RENDER_BOOL("No Specular Map", Lab_Specmap_override);
}
if (Cmdline_env) {
ADD_RENDER_BOOL("No Environment Map", Lab_Envmap_override);
}
if (Cmdline_normal) {
ADD_RENDER_BOOL("No Normal Map", Lab_Normalmap_override);
}
if (Cmdline_height) {
ADD_RENDER_BOOL("No Height Map", Lab_Heightmap_override);
}
ADD_RENDER_BOOL("No Team Colors", Lab_Miscmap_override);
ADD_RENDER_BOOL("No Glow Points", Glowpoint_override);
// model flags
ADD_RENDER_BOOL("Wireframe", Lab_render_wireframe);
ADD_RENDER_BOOL("No Lighting", Lab_render_without_light);
ADD_RENDER_BOOL("Show Full Detail", Lab_render_show_detail);
ADD_RENDER_BOOL("Show Thrusters", Lab_render_show_thrusters);
ADD_RENDER_BOOL("Show Ship Weapons", Lab_render_show_weapons);
ADD_RENDER_FLAG("Initial Rotation", Lab_viewer_flags, LAB_FLAG_INITIAL_ROTATION);
ADD_RENDER_FLAG("Show Destroyed Subsystems", Lab_viewer_flags, LAB_FLAG_DESTROYED_SUBSYSTEMS);
ADD_RENDER_BOOL("Emissive Lighting", Lab_emissive_light_override);
ambient_sldr = new Slider("Ambient Factor", 0, 128, 0, y + 2, labviewer_render_options_set_ambient_factor, Lab_render_options_window->GetWidth());
ambient_sldr->SetSliderValue((float)Cmdline_ambient_factor);
Lab_render_options_window->AddChild(ambient_sldr);
y += ambient_sldr->GetHeight() + 1;
direct_sldr = new Slider("Direct. Lights", 0.0f, 2.0f, 0, y + 2, labviewer_render_options_set_static_light_factor, Lab_render_options_window->GetWidth());
direct_sldr->SetSliderValue(static_light_factor);
Lab_render_options_window->AddChild(direct_sldr);
y += direct_sldr->GetHeight() + 1;
bloom_sldr = new Slider("Bloom", 0, 200, 0, y + 2, labviewer_render_options_set_bloom, Lab_render_options_window->GetWidth());
bloom_sldr->SetSliderValue((float)Cmdline_bloom_intensity);
Lab_render_options_window->AddChild(bloom_sldr);
y += bloom_sldr->GetHeight() + 1;
// start tree
cmp = (Tree*)Lab_render_options_window->AddChild(
new Tree("Detail Options Tree", 0, y + 2, nullptr, Lab_render_options_window->GetWidth()));
// 3d hardware texture slider options
ctip = cmp->AddItem(nullptr, "3D Hardware Textures", 0, false);
cmp->AddItem(ctip, "Minimum", 0, false, labviewer_change_detail_texture);
cmp->AddItem(ctip, "Low", 1, false, labviewer_change_detail_texture);
cmp->AddItem(ctip, "Medium", 2, false, labviewer_change_detail_texture);
cmp->AddItem(ctip, "High", 3, false, labviewer_change_detail_texture);
cmp->AddItem(ctip, "Maximum", 4, false, labviewer_change_detail_texture);
// set close function
Lab_render_options_window->SetCloseFunction(labviewer_close_render_options_window);
}
// ------------------------- Material Override Window ------------------------------
void labviewer_close_material_override_window(GUIObject* /*caller*/)
{
Lab_material_override_window = nullptr;
Basemap_color_override_set = false;
Basemap_color_override[0] = 0.0f;
Basemap_color_override[1] = 0.0f;
Basemap_color_override[2] = 0.0f;
Basemap_color_override[3] = 1.0f;
Glowmap_color_override_set = false;
Glowmap_color_override[0] = 0.0f;
Glowmap_color_override[1] = 0.0f;
Glowmap_color_override[2] = 0.0f;
Specmap_color_override_set = false;
Specmap_color_override[0] = 0.0f;
Specmap_color_override[1] = 0.0f;
Specmap_color_override[2] = 0.0f;
}
void labviewer_set_material_override_diffuse_red(Slider* caller)
{
Basemap_color_override[0] = caller->GetSliderValue() / 255.0f;
}
void labviewer_set_material_override_diffuse_green(Slider* caller)
{
Basemap_color_override[1] = caller->GetSliderValue() / 255.0f;
}
void labviewer_set_material_override_diffuse_blue(Slider* caller)
{
Basemap_color_override[2] = caller->GetSliderValue() / 255.0f;
}
void labviewer_set_material_override_glow_red(Slider* caller)
{
Glowmap_color_override[0] = caller->GetSliderValue() / 255.0f;
}
void labviewer_set_material_override_glow_green(Slider* caller)
{
Glowmap_color_override[1] = caller->GetSliderValue() / 255.0f;
}
void labviewer_set_material_override_glow_blue(Slider* caller)
{
Glowmap_color_override[2] = caller->GetSliderValue() / 255.0f;
}
void labviewer_set_material_override_specular_red(Slider* caller)
{
Specmap_color_override[0] = caller->GetSliderValue() / 255.0f;
}
void labviewer_set_material_override_specular_green(Slider* caller)
{
Specmap_color_override[1] = caller->GetSliderValue() / 255.0f;
}
void labviewer_set_material_override_specular_blue(Slider* caller)
{
Specmap_color_override[2] = caller->GetSliderValue() / 255.0f;
}
void labviewer_set_material_override_specular_gloss(Slider* caller)
{
Gloss_override = caller->GetSliderValue() / 255.0f;
}
void labviewer_make_material_override_window(Button* /*caller*/)
{
Checkbox* cbp;
Slider* sldr;
int y = 0;
if (Lab_material_override_window != nullptr) {
return;
}
Lab_material_override_window =
(Window*)Lab_screen->Add(new Window("Material Override", gr_screen.max_w - 300, 200));
Assert(Lab_material_override_window != nullptr);
// add all of the flags that we want/need...
// map related flags
cbp = (Checkbox*)Lab_material_override_window->AddChild(new Checkbox("Override Diffuse Color", 2, y));
cbp->SetBool(&Basemap_color_override_set);
y += cbp->GetHeight() + 1;
sldr = (Slider*)Lab_material_override_window->AddChild(
new Slider("Red", 0.0f, 255.0f, 0, y + 2, labviewer_set_material_override_diffuse_red, 275));
y += sldr->GetHeight() + 1;
sldr = (Slider*)Lab_material_override_window->AddChild(
new Slider("Green", 0.0f, 255.0f, 0, y + 2, labviewer_set_material_override_diffuse_green, 275));
y += sldr->GetHeight() + 1;
sldr = (Slider*)Lab_material_override_window->AddChild(
new Slider("Blue", 0.0f, 255.0f, 0, y + 2, labviewer_set_material_override_diffuse_blue, 275));
y += sldr->GetHeight() + 1;
if (Cmdline_glow) {
cbp = (Checkbox*)Lab_material_override_window->AddChild(new Checkbox("Override Glow Color", 2, y));
cbp->SetBool(&Glowmap_color_override_set);
y += cbp->GetHeight() + 1;
sldr = (Slider*)Lab_material_override_window->AddChild(
new Slider("Red", 0.0f, 255.0f, 0, y + 2, labviewer_set_material_override_glow_red, 275));
y += sldr->GetHeight() + 1;
sldr = (Slider*)Lab_material_override_window->AddChild(
new Slider("Green", 0.0f, 255.0f, 0, y + 2, labviewer_set_material_override_glow_green, 275));
y += sldr->GetHeight() + 1;
sldr = (Slider*)Lab_material_override_window->AddChild(
new Slider("Blue", 0.0f, 255.0f, 0, y + 2, labviewer_set_material_override_glow_blue, 275));
y += sldr->GetHeight() + 1;
}
if (Cmdline_spec) {
cbp = (Checkbox*)Lab_material_override_window->AddChild(new Checkbox("Override Spec Color", 2, y));
cbp->SetBool(&Specmap_color_override_set);
y += cbp->GetHeight() + 1;
sldr = (Slider*)Lab_material_override_window->AddChild(
new Slider("Red", 0.0f, 255.0f, 0, y + 2, labviewer_set_material_override_specular_red, 275));
y += sldr->GetHeight() + 1;
sldr = (Slider*)Lab_material_override_window->AddChild(
new Slider("Green", 0.0f, 255.0f, 0, y + 2, labviewer_set_material_override_specular_green, 275));
y += sldr->GetHeight() + 1;
sldr = (Slider*)Lab_material_override_window->AddChild(
new Slider("Blue", 0.0f, 255.0f, 0, y + 2, labviewer_set_material_override_specular_blue, 275));
y += sldr->GetHeight() + 1;
cbp = (Checkbox*)Lab_material_override_window->AddChild(new Checkbox("Override Gloss", 2, y));
cbp->SetBool(&Gloss_override_set);
y += cbp->GetHeight() + 1;
sldr = (Slider*)Lab_material_override_window->AddChild(
new Slider("Gloss", 0.0f, 255.0f, 0, y + 2, labviewer_set_material_override_specular_gloss, 275));
y += sldr->GetHeight() + 1;
}
// set close function
Lab_material_override_window->SetCloseFunction(labviewer_close_material_override_window);
}
// ------------------------- Description Window ------------------------------
void labviewer_close_desc_window(GUIObject* /*caller*/)
{
Lab_description_text = nullptr;
Lab_description_window = nullptr;
}
void labviewer_update_desc_window()
{
if ((Lab_description_window == nullptr) || (Lab_description_text == nullptr)) {
return;
}
if (Lab_selected_index != -1) {
if (Lab_mode == LAB_MODE_SHIP) {
Lab_description_window->SetCaption(Ship_info[Lab_selected_index].name);
if (Ship_info[Lab_selected_index].tech_desc != nullptr) {
Lab_description_text->SetText(Ship_info[Lab_selected_index].tech_desc);
} else {
Lab_description_text->SetText("No description available.");
}
} else if (Lab_mode == LAB_MODE_WEAPON) {
Lab_description_window->SetCaption(Weapon_info[Lab_selected_index].get_display_name());
if (Weapon_info[Lab_selected_index].tech_desc != nullptr) {
Lab_description_text->SetText(Weapon_info[Lab_selected_index].tech_desc);
} else {
Lab_description_text->SetText("No description available.");
}
}
}
}
void labviewer_make_desc_window(Button* /*caller*/)
{
if (Lab_description_window != nullptr) {
return;
}
Lab_description_window = (Window*)Lab_screen->Add(
new Window("Description", gr_screen.center_offset_x + gr_screen.center_w - gr_screen.center_w / 3 - 50,
gr_screen.center_offset_y + gr_screen.center_h - gr_screen.center_h / 6 - 50, gr_screen.center_w / 3,
gr_screen.center_h / 6));
Lab_description_text =
(Text*)Lab_description_window->AddChild(new Text("Description Text", "No ship selected.", 0, 0));
labviewer_update_desc_window();
Lab_description_window->SetCloseFunction(labviewer_close_desc_window);
}
// ------------------------ Ships Window -----------------------------------
void labviewer_make_ship_window(Button* /*caller*/)
{
GUIObject* cbp;
TreeItem* stip;
int x, idx;
if (Lab_mode == LAB_MODE_SHIP) {
return;
}
// switch the class window to ship mode
labviewer_set_class_window(LAB_MODE_SHIP);
if ((Lab_class_window == nullptr) || (Lab_class_toolbar == nullptr)) {
Int3();
Lab_mode = LAB_MODE_SHIP;
return;
}
// populate the class toolbar
x = 0;
cbp = Lab_class_toolbar->AddChild(new Button("Class Description", x, 0, labviewer_make_desc_window));
x += cbp->GetWidth() + 10;
cbp = Lab_class_toolbar->AddChild(new Button("Class Options", x, 0, labviewer_make_flags_window));
x += cbp->GetWidth() + 10;
cbp = Lab_class_toolbar->AddChild(new Button("Class Variables", x, 0, labviewer_make_variables_window));
// populate ship class window
ship_tree = (Tree*)Lab_class_window->AddChild(new Tree("Ship Tree", 0, 0));
if (Lab_species_nodes != nullptr) {
for (idx = 0; idx < (int)Species_info.size(); idx++) {
Lab_species_nodes[idx]->ClearAllItems();
}
delete[] Lab_species_nodes;
Lab_species_nodes = nullptr;
}
// in case the window had already been opened
ship_list_endpoints.clear();
for (int i = 0; i < (int)Species_info.size(); i++) {
ship_list_endpoints.emplace_back(std::make_pair(nullptr, nullptr));
}
Lab_species_nodes = new TreeItem*[Species_info.size() + 1];
// Add species nodes
for (idx = 0; idx < (int)Species_info.size(); idx++) {
Lab_species_nodes[idx] = ship_tree->AddItem(nullptr, Species_info[idx].species_name, 0, false);
}
// Just in case. I don't actually think this is possible though.
Lab_species_nodes[Species_info.size()] = ship_tree->AddItem(nullptr, "Other", 0, false);
// Now add the ships
for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it) {
if ((it->species >= 0) && (it->species < (int)Species_info.size())) {
stip = Lab_species_nodes[it->species];
} else {
stip = Lab_species_nodes[Species_info.size()];
}
TreeItem* new_ship_item = ship_tree->AddItem(stip, it->name, (int)std::distance(Ship_info.cbegin(), it), false,
labviewer_change_ship);
if (ship_list_endpoints[it->species].first == nullptr) {
ship_list_endpoints[it->species].first = new_ship_item;
}
ship_list_endpoints[it->species].second = new_ship_item;
// cmp->AddItem(ctip, "Debris", 99, false, labviewer_change_ship_lod);
}
// if any nodes are empty, just add a single "<none>" entry so we know that species doesn't have anything yet
// No the <= is not a mistake :)
for (idx = 0; idx < (int)Species_info.size(); idx++) {
if (!Lab_species_nodes[idx]->HasChildren()) {
ship_tree->AddItem(Lab_species_nodes[idx], "<none>", 0, false, nullptr);
}
}
// if the "Other" entry doesn't contain anything then just delete it
if (!Lab_species_nodes[Species_info.size()]->HasChildren()) {
delete Lab_species_nodes[Species_info.size()];
}
// and... we're done!
Lab_mode = LAB_MODE_SHIP;
// populate the flags window, if it exists
// (NOTE: must be done *after* Lab_mode is set properly)
if (Lab_flags_window != nullptr) {
Lab_flags_window->SetCaption("Ship Flags");
labviewer_populate_flags_window();
labviewer_update_flags_window();
}
if (Lab_description_window != nullptr) {
labviewer_update_desc_window();
}
if (Lab_variables_window != nullptr) {
Lab_variables_window->SetCaption("Ship Variables");
labviewer_populate_variables_window();
labviewer_update_variables_window();
}
}
void labviewer_change_ship_lod(Tree* caller)
{
if (caller == nullptr) {
return;
}
int ship_index = caller->GetSelectedItem()->GetData();
Assert(ship_index >= 0);
if (Lab_selected_object == -1) {
// Goober5000 - The lab loads subsystems into its special lab-specific vector, but normally subsystems are
// loaded into the Ship_info entry. Note also that models are only loaded once each. If a lab model was
// previously loaded using the "lightweight" method, ship_create will not find any of the subsystems it is
// looking for. So we have to make sure the model is only loaded for the purposes of error-checking and then
// immediately cleared so that it can be subsequently loaded the lab way.
// reset any existing model/bitmap that is showing
labviewer_change_model(nullptr);
} else {
obj_delete_all();
}
// clean up any running particle effects
particle::kill_all();
Lab_selected_object = ship_create(&vmd_identity_matrix, &Lab_model_pos, ship_index);
auto ship_objp = &Ships[Objects[Lab_selected_object].instance];
auto ship_infop = &Ship_info[ship_objp->ship_info_index];
// This is normally set during mission parse
ship_objp->special_exp_damage = -1;
// If the ship class defines replacement textures, load them and apply them to the ship
// load the texture
auto replacements = SCP_vector<texture_replace>();
for (auto tr : ship_infop->replacement_textures) {
if (!stricmp(tr.new_texture, "invisible"))
{
// invisible is a special case
tr.new_texture_id = REPLACE_WITH_INVISIBLE;
}
else
{
// try to load texture or anim as normal
tr.new_texture_id = bm_load_either(tr.new_texture);
}
replacements.push_back(tr);
}
ship_objp->apply_replacement_textures(replacements);
lab_cam_distance = Objects[Lab_selected_object].radius * 1.6f;
Lab_last_selected_ship = Lab_selected_index;
labviewer_change_model(Ship_info[ship_index].pof_file, ship_index, ship_index);
// update the displayed POF filename
// moved out of labviewer_change_model which doesn't necessarily do much of anything after the first ship is loaded
if (Lab_model_num >= 0) {
strcpy_s(Lab_model_filename, Ship_info[ship_index].pof_file);
} else {
memset(Lab_model_filename, 0, sizeof(Lab_model_filename));
}
if (Ship_info[ship_index].uses_team_colors) {
Lab_team_color = Ship_info[ship_index].default_team_name;
} else {
Lab_team_color = "<none>";
}
labviewer_update_desc_window();
labviewer_update_flags_window();
labviewer_update_variables_window();
labviewer_recalc_camera();
Lab_viewer_flags &= ~LAB_FLAG_SHIP_EXPLODING;
}
void labviewer_change_ship(Tree* caller)
{
Lab_last_selected_object = caller->GetSelectedItem();
labviewer_change_ship_lod(caller);
}
// --------------------------- Weapons Window --------------------------------
void labviewer_show_tech_model(Tree* caller)
{
int weap_index = (int)(caller->GetSelectedItem()->GetParentItem()->GetData());
Assert(weap_index >= 0);
labviewer_change_model(Weapon_info[weap_index].tech_model, caller->GetSelectedItem()->GetData(), weap_index);
}
void labviewer_show_external_model(Tree* caller)
{
int weap_index = (int)(caller->GetSelectedItem()->GetParentItem()->GetData());
Assert(weap_index >= 0);
labviewer_change_model(Weapon_info[weap_index].external_model_name, caller->GetSelectedItem()->GetData(),
weap_index);
}
extern void weapon_load_bitmaps(int weapon_index);
void labviewer_change_weapon(Tree* caller)
{
if (caller == nullptr) {
return;
}
int weap_index = caller->GetSelectedItem()->GetData();
Lab_last_selected_object = caller->GetSelectedItem();
Assert(weap_index >= 0);
if (Lab_selected_object != -1)
obj_delete(Lab_selected_object);
if (!(Weapon_info[weap_index].wi_flags[Weapon::Info_Flags::Beam])) {
Lab_selected_object = weapon_create(&vmd_zero_vector, &vmd_identity_matrix, weap_index, -1);
}
lab_cam_distance = Objects[Lab_selected_object].radius * 20.0f;
Lab_selected_index = weap_index;
Lab_last_selected_weapon = Lab_selected_index;
labviewer_update_desc_window();
labviewer_update_flags_window();
labviewer_update_variables_window();
labviewer_recalc_camera();
}
// weapon window create function
void labviewer_make_weap_window(Button* /*caller*/)
{
GUIObject* cbp;
TreeItem* stip;
int x;
if (Lab_mode == LAB_MODE_WEAPON) {
return;
}
// switch the class window to weapon mode
labviewer_set_class_window(LAB_MODE_WEAPON);
if ((Lab_class_window == nullptr) || (Lab_class_toolbar == nullptr)) {
Int3();
Lab_mode = LAB_MODE_WEAPON;
return;
}
// populate the weapons toolbar
x = 0;
cbp = Lab_class_toolbar->AddChild(new Button("Class Description", x, 0, labviewer_make_desc_window));
x += cbp->GetWidth() + 10;
cbp = Lab_class_toolbar->AddChild(new Button("Class Options", x, 0, labviewer_make_flags_window));
x += cbp->GetWidth() + 10;
cbp = Lab_class_toolbar->AddChild(new Button("Class Variables", x, 0, labviewer_make_variables_window));
weap_list_endpoints.clear();
for (int i = 0; i < Num_weapon_subtypes; i++) {
weap_list_endpoints.emplace_back(std::make_pair(nullptr, nullptr));
}
// populate the weapons window
weap_tree = (Tree*)Lab_class_window->AddChild(new Tree("Weapon Tree", 0, 0));
// Unfortunately these are hardcoded
auto type_nodes = new TreeItem*[Num_weapon_subtypes];
// Add type nodes
for (int i = 0; i < Num_weapon_subtypes; ++i) {
type_nodes[i] = weap_tree->AddItem(nullptr, Weapon_subtype_names[i], 0, false);
}
// Now add the weapons
for (int i = 0; i < weapon_info_size(); ++i) {
auto &wi = Weapon_info[i];
if (wi.subtype == WP_UNUSED) {
continue;
} else if (wi.subtype >= Num_weapon_subtypes) {
Warning(LOCATION, "Invalid weapon subtype found on weapon %s", wi.name);
continue;
}
if (wi.wi_flags[Weapon::Info_Flags::Beam]) {
stip = type_nodes[WP_BEAM];
} else {
stip = type_nodes[wi.subtype];
}
TreeItem* new_weap_item =
weap_tree->AddItem(stip, wi.get_display_name(), i, false, labviewer_change_weapon);
if (weap_list_endpoints[wi.subtype].first == nullptr) {
weap_list_endpoints[wi.subtype].first = new_weap_item;
}
weap_list_endpoints[wi.subtype].second = new_weap_item;
// if (wi.tech_model[0] != '\0') {
// cmp->AddItem(cwip, "Tech Model", 0, false, labviewer_show_tech_model);
//}
// if (wi.external_model_name[0] != '\0') {
// cmp->AddItem(cwip, "External Model", 0, false, labviewer_show_external_model);
//}
}
// Get rid of any empty nodes
for (int i = 0; i < Num_weapon_subtypes; ++i) {
if (!type_nodes[i]->HasChildren()) {
delete type_nodes[i];
}
}
delete[] type_nodes;
Lab_mode = LAB_MODE_WEAPON;
// populate the flags window, if it exists
// (NOTE: must be done *after* Lab_mode is set properly)
if (Lab_flags_window != nullptr) {
Lab_flags_window->SetCaption("Weapon Flags");
labviewer_populate_flags_window();
labviewer_update_flags_window();
}
if (Lab_description_window != nullptr) {
labviewer_update_desc_window();
}
if (Lab_variables_window != nullptr) {
Lab_variables_window->SetCaption("Weapon Variables");
labviewer_populate_variables_window();
labviewer_update_variables_window();
}
}
// ----------------------------- Backgrounds -----------------------------------
static TreeItem** Mission_directories = nullptr;
size_t Num_mission_directories = 0;
char skybox_model[MAX_FILENAME_LEN];
int skybox_flags;
int ambient_light_level;
extern const char* Neb2_filenames[];
char envmap_name[MAX_FILENAME_LEN];
const char* mission_ext_list[] = {".fs2"};
SCP_string get_directory_or_vp(const char* path)
{
SCP_string result(path);
// Is this a mission in a directory?
size_t found = result.find("data" DIR_SEPARATOR_STR "missions");
if (found == std::string::npos) // Guess not
{
found = result.find(".vp");
}
auto directory_name_pos = result.rfind(DIR_SEPARATOR_CHAR, found - strlen(DIR_SEPARATOR_STR) - 1);
result = result.substr(directory_name_pos, found - directory_name_pos);
found = result.find(DIR_SEPARATOR_CHAR);
// Strip directory separators
while (found != std::string::npos) {
result.erase(found, strlen(DIR_SEPARATOR_STR));
found = result.find(DIR_SEPARATOR_CHAR);
}
return result;
}
void labviewer_change_background_actual()
{
matrix skybox_orientation;
stars_pre_level_init(true);
vm_set_identity(&skybox_orientation);
// (DahBlount) - Remember to load the debris anims
stars_load_debris(false);
if (Lab_selected_mission != "None") {
read_file_text((Lab_selected_mission + ".fs2").c_str(), CF_TYPE_MISSIONS);
reset_parse();
flagset<Mission::Mission_Flags> flags;
skip_to_start_of_string("+Flags");
if (optional_string("+Flags:"))
stuff_flagset(&flags);
// Are we using a skybox?
skip_to_start_of_string_either("$Skybox Model:", "#Background bitmaps");
strcpy_s(skybox_model, "");
if (optional_string("$Skybox Model:")) {
stuff_string(skybox_model, F_NAME, MAX_FILENAME_LEN);
if (optional_string("+Skybox Orientation:")) {
stuff_matrix(&skybox_orientation);
}
if (optional_string("+Skybox Flags:")) {
skybox_flags = 0;
stuff_int(&skybox_flags);
} else {
skybox_flags = DEFAULT_NMODEL_FLAGS;
}
stars_set_background_model(skybox_model, nullptr, skybox_flags);
stars_set_background_orientation(&skybox_orientation);
skip_to_start_of_string("#Background bitmaps");
}
if (optional_string("#Background bitmaps")) {
required_string("$Num stars:");
stuff_int(&Num_stars);
if (Num_stars >= MAX_STARS)
Num_stars = MAX_STARS;
required_string("$Ambient light level:");
stuff_int(&ambient_light_level);
if (ambient_light_level == 0) {
ambient_light_level = DEFAULT_AMBIENT_LIGHT_LEVEL;
}
gr_set_ambient_light(ambient_light_level & 0xff, (ambient_light_level >> 8) & 0xff,
(ambient_light_level >> 16) & 0xff);
strcpy_s(Neb2_texture_name, "Eraseme3");
Neb2_poof_flags = ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5));
if (optional_string("+Neb2:")) {
stuff_string(Neb2_texture_name, F_NAME, MAX_FILENAME_LEN);
required_string("+Neb2Flags:");
stuff_int(&Neb2_poof_flags);
if (flags[Mission::Mission_Flags::Fullneb]) {
neb2_post_level_init();
}
}
if (flags[Mission::Mission_Flags::Fullneb]) {
// no regular nebula stuff
nebula_close();
} else {
Nebula_index = -1;
if (optional_string("+Nebula:")) {
char str[MAX_FILENAME_LEN];
int z;
stuff_string(str, F_NAME, MAX_FILENAME_LEN);
// parse the proper nebula type (full or not)
for (z = 0; z < NUM_NEBULAS; z++) {
if (flags[Mission::Mission_Flags::Fullneb]) {
if (!stricmp(str, Neb2_filenames[z])) {
Nebula_index = z;
break;
}
} else {
if (!stricmp(str, Nebula_filenames[z])) {
Nebula_index = z;
break;
}
}
}
if (z == NUM_NEBULAS)
WarningEx(LOCATION, "Unknown nebula %s!", str);
if (optional_string("+Color:")) {
stuff_string(str, F_NAME, MAX_FILENAME_LEN);
for (z = 0; z < NUM_NEBULA_COLORS; z++) {
if (!stricmp(str, Nebula_colors[z])) {
Mission_palette = z;
break;
}
}
}
if (z == NUM_NEBULA_COLORS)
WarningEx(LOCATION, "Unknown nebula color %s!", str);
if (optional_string("+Pitch:")) {
stuff_int(&Nebula_pitch);
} else {
Nebula_pitch = 0;
}
if (optional_string("+Bank:")) {
stuff_int(&Nebula_bank);
} else {
Nebula_bank = 0;
}
if (optional_string("+Heading:")) {
stuff_int(&Nebula_heading);
} else {
Nebula_heading = 0;
}
}
if (Nebula_index >= 0) {
nebula_init(Nebula_filenames[Nebula_index], Nebula_pitch, Nebula_bank, Nebula_heading);
} else {
nebula_close();
}
}
stars_load_debris(flags[Mission::Mission_Flags::Fullneb]);
extern void parse_one_background(background_t * background);
while (optional_string("$Bitmap List:") || check_for_string("$Sun:") || check_for_string("$Starbitmap:")) {
Backgrounds.emplace_back();
parse_one_background(&Backgrounds.back());
}
stars_load_first_valid_background();
if (optional_string("$Environment Map:")) {
stuff_string(envmap_name, F_NAME, MAX_FILENAME_LEN);
}
const int size = 512;
int gen_flags = (BMP_FLAG_RENDER_TARGET_STATIC | BMP_FLAG_CUBEMAP | BMP_FLAG_RENDER_TARGET_MIPMAP);
if (!Cmdline_env) {
return;
}
if (gr_screen.envmap_render_target >= 0) {
if (!bm_release(gr_screen.envmap_render_target, 1)) {
Warning(LOCATION, "Unable to release environment map render target.");
}
gr_screen.envmap_render_target = -1;
}
if (strlen(envmap_name)) {
// Load the mission map so we can use it later
ENVMAP = bm_load(The_mission.envmap_name);
return;
}
gr_screen.envmap_render_target = bm_make_render_target(size, size, gen_flags);
}
} else {
// (DahBlount) - This spot should be used to disable rendering features that only apply to missions.
Motion_debris_override = true;
Num_stars = 0;
}
}
void labviewer_change_background(Tree* caller)
{
Lab_selected_mission = caller->GetSelectedItem()->Name;
labviewer_change_background_actual();
}
void lab_background_window_close(GUIObject* /*caller*/) { Lab_background_window = nullptr; }
void labviewer_make_background_window(Button* /*caller*/)
{
if (Lab_background_window != nullptr)
return;
Lab_background_window = (Window*)Lab_screen->Add(
new Window("Mission Backgrounds", gr_screen.center_offset_x + 250, gr_screen.center_offset_y + 50));
Lab_background_window->SetCloseFunction(lab_background_window_close);
SCP_vector<SCP_string> missions;
cf_get_file_list(missions, CF_TYPE_MISSIONS, NOX("*.fs2"));
SCP_map<SCP_string, SCP_vector<SCP_string>> directories;
for (const auto& filename : missions) {
auto res = cf_find_file_location((filename + ".fs2").c_str(), CF_TYPE_MISSIONS);
if (res.found) {
auto location = get_directory_or_vp(res.full_name.c_str());
directories[location].push_back(filename);
}
}
Num_mission_directories = directories.size();
Mission_directories = new TreeItem*[Num_mission_directories];
Tree* missiontree = (Tree*)Lab_background_window->AddChild(new Tree("Missions", 0, 0));
missiontree->AddItem(nullptr, "None", 0, true, labviewer_change_background);
int i = 0;
for (auto directory : directories) {
auto directoryItem = Mission_directories[i];
directoryItem = missiontree->AddItem(nullptr, directory.first);
for (const auto& Lab_mission : directory.second) {
missiontree->AddItem(directoryItem, Lab_mission, 0, true, labviewer_change_background);
}
}
}
// ----------------------------- Actions window --------------------------------
void lab_actions_window_close(GUIObject* /*caller*/) {
Lab_actions_window = nullptr;
}
void lab_subsystems_window_close(GUIObject* /*caller*/) {
Lab_subsystems_window = nullptr;
}
void lab_loadout_window_close(GUIObject* /*caller*/) {
Lab_loadout_window = nullptr;
}
void lab_weapon_fire_window_close(GUIObject* /*caller*/) {
Lab_weapon_fire_window = nullptr;
}
void lab_animations_window_close(GUIObject* /*caller*/) {
Lab_trigger_animations_window = nullptr;
}
// Returns true if it is safe to access the Ships array
bool action_is_safe_for_ships() {
return Lab_selected_object != -1 && Objects[Lab_selected_object].type == OBJ_SHIP;
}
void labviewer_actions_destroy_ship(Button* /*caller*/) {
if (action_is_safe_for_ships()) {
auto obj = &Objects[Lab_selected_object];
obj->flags.remove(Object::Object_Flags::Player_ship);
ship_self_destruct(obj);
Lab_viewer_flags |= LAB_FLAG_SHIP_EXPLODING;
}
}
void labviewer_actions_destroy_subsystem(Tree* caller) {
auto selected_subsys_index = caller->GetSelectedItem()->GetData();
if (action_is_safe_for_ships()) {
auto sp = &Ships[Objects[Lab_selected_object].instance];
auto ssp = GET_FIRST(&sp->subsys_list);
auto subsys_index = 0;
while (ssp != END_OF_LIST(&sp->subsys_list)) {
if (subsys_index == selected_subsys_index) {
do_subobj_destroyed_stuff(sp, ssp, nullptr);
}
ssp = GET_NEXT(ssp);
++subsys_index;
}
}
}
void labviewer_actions_change_primary(Tree* caller) {
auto weapon_name = caller->GetSelectedItem()->Name;
auto bank_num = caller->GetSelectedItem()->GetData();
auto weapon_index = weapon_info_lookup(weapon_name.c_str());
auto sp = &Ships[Objects[Lab_selected_object].instance];
sp->weapons.primary_bank_weapons[bank_num] = weapon_index;
if (Weapon_info[weapon_index].wi_flags[Weapon::Info_Flags::Ballistic]) {
sp->weapons.primary_bank_ammo[bank_num] = 10000;
}
}
void labviewer_actions_change_secondary(Tree* caller) {
auto weapon_name = caller->GetSelectedItem()->Name;
auto bank_num = caller->GetSelectedItem()->GetData();
auto weapon_index = weapon_info_lookup(weapon_name.c_str());
auto sp = &Ships[Objects[Lab_selected_object].instance];
sp->weapons.secondary_bank_weapons[bank_num] = weapon_index;
}
void labviewer_fill_subsystem_menu() {
if (Lab_subsystems_window == nullptr) return;
auto subsys_tree = (Tree*)Lab_subsystems_window->AddChild(new Tree("Subsystem List", 0, 0));
if (action_is_safe_for_ships()) {
auto sp = &Ships[Objects[Lab_selected_object].instance];
auto ssp = GET_FIRST(&sp->subsys_list);
auto subsys_index = 0;
while (ssp != END_OF_LIST(&sp->subsys_list)) {
subsys_tree->AddItem(nullptr, ssp->system_info->name, subsys_index, true, labviewer_actions_destroy_subsystem);
ssp = GET_NEXT(ssp);
++subsys_index;
}
}
}
void labviewer_actions_make_subsys_window(Button* /*caller*/) {
if (Lab_subsystems_window != nullptr)
return;
Lab_subsystems_window = (Window*)Lab_screen->Add(
new Window("Destroy Subsystems", gr_screen.center_offset_x + 400, gr_screen.center_offset_y + 50)
);
Lab_subsystems_window->SetCloseFunction(lab_subsystems_window_close);
labviewer_fill_subsystem_menu();
}
void labviewer_fill_loadout_window() {
if (Lab_loadout_window == nullptr)
return;
auto weapons_tree = (Tree*)Lab_loadout_window->AddChild(new Tree("Weapons stuff", 0, 0));
if (action_is_safe_for_ships()) {
auto sp = &Ships[Objects[Lab_selected_object].instance];
auto sip = &Ship_info[sp->ship_info_index];
if (sip->is_flyable()) {
auto primaries_head = weapons_tree->AddItem(nullptr, "Primaries", 0, false);
auto secondaries_head = weapons_tree->AddItem(nullptr, "Secondaries", 0, false);
for (auto bank_num = 0; bank_num < sip->num_primary_banks; ++bank_num) {
SCP_string bank_string;
sprintf(bank_string, "Bank %i", bank_num);
auto bank_head = weapons_tree->AddItem(primaries_head, bank_string, 0, false);
auto tabled_weapons_head = weapons_tree->AddItem(bank_head, "Tabled weapons", 0, false);
auto others_head = weapons_tree->AddItem(bank_head, "Other Primaries", 0, false);
auto beams_head = weapons_tree->AddItem(others_head, "Beams", 0, false);
auto ballistics_head = weapons_tree->AddItem(others_head, "Ballistics", 0, false);
auto player_allowed_ballistics = weapons_tree->AddItem(ballistics_head, "Player Allowed", 0, false);
auto lasers_head = weapons_tree->AddItem(others_head, "Lasers", 0, false);
auto player_allowed_lasers = weapons_tree->AddItem(lasers_head, "Player Allowed", 0, false);
auto capitals_head = weapons_tree->AddItem(bank_head, "Capital Ship weapons", 0, false);
auto n_weapons = MIN(Weapon_info.size(), MAX_WEAPON_TYPES);
for (size_t j = 0; j < n_weapons; ++j) {
auto wip = &Weapon_info[j];
if (wip->subtype == WP_LASER || wip->subtype == WP_BEAM) {
if (sip->allowed_weapons[j] != 0) {
weapons_tree->AddItem(tabled_weapons_head, wip->name, bank_num, true, labviewer_actions_change_primary);
}
else {
TreeItem* head_item = nullptr;
if (wip->wi_flags[Weapon::Info_Flags::Beam] || wip->subtype == WP_BEAM) {
head_item = beams_head;
} else if (wip->wi_flags[Weapon::Info_Flags::Huge, Weapon::Info_Flags::Supercap]) {
head_item = capitals_head;
} else if (wip->subtype == WP_LASER) {
if (wip->wi_flags[Weapon::Info_Flags::Ballistic]) {
if (wip->wi_flags[Weapon::Info_Flags::Player_allowed])
head_item = player_allowed_ballistics;
else
head_item = ballistics_head;
} else {
if (wip->wi_flags[Weapon::Info_Flags::Player_allowed])
head_item = player_allowed_lasers;
else
head_item = lasers_head;
}
}
if (head_item != nullptr) {
weapons_tree->AddItem(head_item, wip->name, bank_num, true, labviewer_actions_change_primary);
}
}
}
}
}
for (auto i = 0; i < sip->num_secondary_banks; ++i) {
SCP_string bank_string;
sprintf(bank_string, "Bank %i", i);
auto bank_head = weapons_tree->AddItem(secondaries_head, bank_string, 0, false);
auto tabled_weapons_head = weapons_tree->AddItem(bank_head, "Tabled weapons", 0, false);
auto others_head = weapons_tree->AddItem(bank_head, "Others", 0, false);
auto missiles_head = weapons_tree->AddItem(others_head, "Missiles", 0, false);
auto bombs_head = weapons_tree->AddItem(others_head, "Bombs", 0, false);
auto n_weapons = MIN(Weapon_info.size(), MAX_WEAPON_TYPES);
for (size_t j = 0; j < n_weapons; ++j) {
auto wip = &Weapon_info[j];
if (wip->subtype == WP_MISSILE) {
TreeItem* head_item = nullptr;
if (sip->allowed_weapons[j] != 0) {
head_item = tabled_weapons_head;
}
else {
if (wip->wi_flags[Weapon::Info_Flags::Bomb]) {
head_item = bombs_head;
}
else {
head_item = missiles_head;
}
}
weapons_tree->AddItem(head_item, wip->name, i, true, labviewer_actions_change_secondary);
}
}
}
}
}
}
void labviewer_actions_make_loadout_window(Button* /*caller*/) {
if (Lab_loadout_window != nullptr)
return;
Lab_loadout_window = (Window*)Lab_screen->Add(
new Window("Change Loadout", gr_screen.center_offset_x + 400, gr_screen.center_offset_y + 50)
);
Lab_loadout_window->SetCloseFunction(lab_loadout_window_close);
labviewer_fill_loadout_window();
}
void labviewer_fill_fire_weapon_menu() {
if (Lab_weapon_fire_window == nullptr) return;
if (action_is_safe_for_ships()) {
auto sp = &Ships[Objects[Lab_selected_object].instance];
int y = 0;
for (auto i = 0; i < sp->weapons.num_primary_banks; ++i) {
SCP_string bank_string;
sprintf(bank_string, "Primary bank %i", i);
auto cbp = (Checkbox*)Lab_weapon_fire_window->AddChild(new Checkbox((bank_string), 2, y));
cbp->SetFlag(&Lab_fire_primaries, 1 << i);
y += cbp->GetHeight() + 1;
}
for (auto i = 0; i < sp->weapons.num_secondary_banks; ++i) {
SCP_string bank_string;
sprintf(bank_string, "Secondary bank %i", i);
auto cbp = (Checkbox*)Lab_weapon_fire_window->AddChild(new Checkbox((bank_string), 2, y));
cbp->SetFlag(&Lab_fire_secondaries, 1 << i);
y += cbp->GetHeight() + 1;
}
}
}
void labviewer_actions_make_weapon_fire_window(Button* /*caller*/) {
if (Lab_weapon_fire_window != nullptr)
return;
Lab_weapon_fire_window = (Window*)Lab_screen->Add(
new Window("Fire weapons", gr_screen.center_offset_x + 400, gr_screen.center_offset_y + 50)
);
Lab_weapon_fire_window->SetCloseFunction(lab_weapon_fire_window_close);
labviewer_fill_fire_weapon_menu();
}
static std::map<AnimationTriggerType, bool> manual_animations = {};
void labviewer_actions_trigger_animation(Tree* caller) {
if (action_is_safe_for_ships()) {
auto shipp = &Ships[Objects[Lab_selected_object].instance];
auto anim_type = static_cast<AnimationTriggerType>(caller->GetSelectedItem()->GetData());
// bool model_anim_start_type(ship *shipp, AnimationTriggerType animation_type, int subtype, int direction, bool instant = false); // for all valid subsystems
model_anim_start_type(shipp, anim_type, 0, manual_animations[anim_type] ? -1 : 1, false);
manual_animations[anim_type] = !manual_animations[anim_type];
}
}
bool triggered_primary_banks[MAX_SHIP_PRIMARY_BANKS];
bool triggered_secondary_banks[MAX_SHIP_SECONDARY_BANKS];
static std::map<AnimationTriggerType, std::map<int, bool>> manual_animation_triggers = {};
void labviewer_actions_reset_animations(Tree* /*caller*/) {
if (action_is_safe_for_ships()) {
auto shipp = &Ships[Objects[Lab_selected_object].instance];
for (auto i = 0; i < MAX_SHIP_PRIMARY_BANKS; ++i) {
if (triggered_primary_banks[i]) {
model_anim_start_type(shipp, AnimationTriggerType::PrimaryBank, i, -1, false);
triggered_primary_banks[i] = false;
}
}
for (auto i = 0; i < MAX_SHIP_SECONDARY_BANKS; ++i) {
if (triggered_secondary_banks[i]) {
model_anim_start_type(shipp, AnimationTriggerType::SecondaryBank, i, -1, false);
triggered_secondary_banks[i] = false;
}
}
for (auto entry: manual_animations) {
if (manual_animations[entry.first]) {
model_anim_start_type(shipp, entry.first, 0, -1, false);
manual_animations[entry.first] = false;
}
}
for (const auto& entry: manual_animation_triggers) {
auto animation_type = entry.first;
auto manual_trigger_map = entry.second;
for (auto manual_trigger : manual_trigger_map) {
if (manual_trigger.second) {
model_anim_start_type(shipp, animation_type, manual_trigger.first, -1, false);
}
}
}
}
}
void labviewer_actions_trigger_primary_bank(Tree* caller) {
if (action_is_safe_for_ships()) {
auto shipp = &Ships[Objects[Lab_selected_object].instance];
auto bank = caller->GetSelectedItem()->GetData();
model_anim_start_type(shipp, AnimationTriggerType::PrimaryBank, bank, triggered_primary_banks[bank] ? -1 : 1, false);
triggered_primary_banks[bank] = !triggered_primary_banks[bank];
}
}
void labviewer_actions_trigger_secondary_bank(Tree* caller) {
if (action_is_safe_for_ships()) {
auto shipp = &Ships[Objects[Lab_selected_object].instance];
auto bank = caller->GetSelectedItem()->GetData();
model_anim_start_type(shipp, AnimationTriggerType::SecondaryBank, bank, triggered_secondary_banks[bank] ? -1 : 1, false);
triggered_secondary_banks[bank] = !triggered_secondary_banks[bank];
}
}
void labviewer_actions_do_triggered_anim(AnimationTriggerType type, int subobj_num, int direction) {
if (action_is_safe_for_ships()) {
auto shipp = &Ships[Objects[Lab_selected_object].instance];
model_anim_start_type(shipp, type, subobj_num, direction);
}
}
void labviewer_actions_trigger_turret_firing(Tree* caller) {
auto subobj_num = caller->GetSelectedItem()->GetData();
auto turret_firing_triggers = manual_animation_triggers[AnimationTriggerType::TurretFiring];
auto direction = turret_firing_triggers[subobj_num] ? -1 : 1;
labviewer_actions_do_triggered_anim(AnimationTriggerType::TurretFiring, subobj_num, direction);
turret_firing_triggers[subobj_num] = !turret_firing_triggers[subobj_num];
}
void labviewer_actions_trigger_turret_fired(Tree* caller) {
auto subobj_num = caller->GetSelectedItem()->GetData();
auto turret_fired_triggers = manual_animation_triggers[AnimationTriggerType::TurretFired];
auto direction = turret_fired_triggers[subobj_num] ? -1 : 1;
labviewer_actions_do_triggered_anim(AnimationTriggerType::TurretFired, subobj_num, direction);
turret_fired_triggers[subobj_num] = !turret_fired_triggers[subobj_num];
}
void labviewer_actions_trigger_scripted(Tree* caller) {
auto subobj_num = caller->GetSelectedItem()->GetData();
auto scripted_anim_triggers = manual_animation_triggers[AnimationTriggerType::Scripted];
auto direction = scripted_anim_triggers[subobj_num] ? -1 : 1;
labviewer_actions_do_triggered_anim(AnimationTriggerType::Scripted, subobj_num, direction);
scripted_anim_triggers[subobj_num] = !scripted_anim_triggers[subobj_num];
}
void labviewer_fill_animations_window() {
if (Lab_trigger_animations_window == nullptr)
return;
if (action_is_safe_for_ships()) {
auto shipp = &Ships[Objects[Lab_selected_object].instance];
auto animations_tree = (Tree*)Lab_trigger_animations_window->AddChild(new Tree("Animations stuff", 0, 0));
std::map<AnimationTriggerType, TreeItem*> subsystem_headers;
animations_tree->AddItem(nullptr, "Reset animation state", 0, true, labviewer_actions_reset_animations);
auto subsystems_head = animations_tree->AddItem(nullptr, "Subsystem triggers");
manual_animation_triggers.clear();
for (const auto& entry : Animation_type_names) {
if (entry.first == AnimationTriggerType::Initial)
continue;
subsystem_headers[entry.first] = animations_tree->AddItem(subsystems_head, entry.second);
}
for (auto i = 0; i < shipp->weapons.num_primary_banks; ++i) {
SCP_string bank_string;
sprintf(bank_string, "Trigger animations for Bank %i", i);
animations_tree->AddItem(subsystem_headers[AnimationTriggerType::PrimaryBank], bank_string, i, true, labviewer_actions_trigger_primary_bank);
}
for (bool& triggered_primary_bank : triggered_primary_banks)
triggered_primary_bank = false;
for (auto i = 0; i < shipp->weapons.num_secondary_banks; ++i) {
SCP_string bank_string;
sprintf(bank_string, "Trigger animations for Bank %i", i);
animations_tree->AddItem(subsystem_headers[AnimationTriggerType::SecondaryBank], bank_string, i, true, labviewer_actions_trigger_secondary_bank);
}
for (bool& triggered_secondary_bank : triggered_secondary_banks)
triggered_secondary_bank = false;
auto ssp = GET_FIRST(&shipp->subsys_list);
auto subsys_index = 0;
// We use these vectors to handle cases where several subsystems have animations defined for the same turret firing/having fired
SCP_vector<int> turret_firing_subsystem_triggers;
SCP_vector<int> turret_fired_subsystem_triggers;
std::map<int, model_subsystem*> stupid_workaround_map; // this is a stupid workaround to avoid having to iterate over the subsys list a billion times
while (ssp != END_OF_LIST(&shipp->subsys_list)) {
stupid_workaround_map[ssp->system_info->subobj_num] = ssp->system_info;
if (ssp->system_info->n_triggers != 0) {
for (auto i = 0; i < ssp->system_info->n_triggers; ++i) {
auto trigger = ssp->system_info->triggers[i];
if (trigger.type == AnimationTriggerType::Initial)
continue;
switch (trigger.type) {
case AnimationTriggerType::TurretFiring:
if (!SCP_vector_contains(turret_firing_subsystem_triggers, trigger.subtype)) {
// For "turret-firing" animations, subtype contains the subobject number of the firing turret
turret_firing_subsystem_triggers.push_back(trigger.subtype);
manual_animation_triggers[trigger.type][trigger.subtype] = false;
}
break;
case AnimationTriggerType::TurretFired:
if (!SCP_vector_contains(turret_fired_subsystem_triggers, trigger.subtype)) {
// Same as above
turret_fired_subsystem_triggers.push_back(trigger.subtype);
manual_animation_triggers[trigger.type][ssp->system_info->subobj_num] = false;
}
break;
case AnimationTriggerType::Scripted:
manual_animation_triggers[trigger.type][ssp->system_info->subobj_num] = false;
animations_tree->AddItem(subsystem_headers[AnimationTriggerType::Scripted], ssp->system_info->name, ssp->system_info->subobj_num, true, labviewer_actions_trigger_scripted);
break;
default:
break;
}
}
}
ssp = GET_NEXT(ssp);
++subsys_index;
}
for (auto subobj_num : turret_firing_subsystem_triggers) {
animations_tree->AddItem(subsystem_headers[AnimationTriggerType::TurretFiring], stupid_workaround_map[subobj_num]->subobj_name, subobj_num, true, labviewer_actions_trigger_turret_firing);
}
for (auto subobj_num : turret_fired_subsystem_triggers) {
animations_tree->AddItem(subsystem_headers[AnimationTriggerType::TurretFired], stupid_workaround_map[subobj_num]->subobj_name, subobj_num, true, labviewer_actions_trigger_turret_fired);
}
auto shipwide_head = animations_tree->AddItem(nullptr, "Shipwide triggers");
for (const auto& entry: Animation_type_names) {
manual_animations[entry.first] = false;
animations_tree->AddItem(shipwide_head, entry.second, static_cast<int>(entry.first), false, labviewer_actions_trigger_animation);
}
}
}
void labviewer_actions_make_animations_window(Button* /*caller*/) {
if (Lab_trigger_animations_window != nullptr)
return;
Lab_trigger_animations_window = (Window*)Lab_screen->Add(
new Window("Trigger animations", gr_screen.center_offset_x + 400, gr_screen.center_offset_y + 50)
);
Lab_trigger_animations_window->SetCloseFunction(lab_animations_window_close);
labviewer_fill_animations_window();
}
void labviewer_fill_actions_menu()
{
int y = 0;
auto btn = new Button("Destroy Ship", 0, y, labviewer_actions_destroy_ship);
Lab_actions_window->AddChild(btn);
y += btn->GetHeight();
btn = new Button("Destroy Subsystems", 0, y, labviewer_actions_make_subsys_window);
Lab_actions_window->AddChild(btn);
y += btn->GetHeight();
btn = new Button("Change Loadout", 0, y, labviewer_actions_make_loadout_window);
Lab_actions_window->AddChild(btn);
y += btn->GetHeight();
btn = new Button("Fire Weapons", 0, y, labviewer_actions_make_weapon_fire_window);
Lab_actions_window->AddChild(btn);
y += btn->GetHeight();
btn = new Button("Trigger animations", 0, y, labviewer_actions_make_animations_window);
Lab_actions_window->AddChild(btn);
y += btn->GetHeight();
}
void labviewer_make_actions_window(Button* /*caller*/)
{
if (Lab_actions_window != nullptr)
return;
if (!Lab_weapons_loaded)
labviewer_load_weapons();
Lab_actions_window = (Window*)Lab_screen->Add(
new Window("Actions", gr_screen.center_offset_x + 250, gr_screen.center_offset_y + 50)
);
Lab_actions_window->SetCloseFunction(lab_actions_window_close);
labviewer_fill_actions_menu();
}
// Called when the viewed ship changes while the action window is open
void labviewer_update_actions_window() {
if (Lab_actions_window != nullptr) {
Lab_actions_window->DeleteChildren();
labviewer_fill_actions_menu();
}
// update the submenus
if (Lab_subsystems_window != nullptr) {
Lab_subsystems_window->DeleteChildren();
labviewer_fill_subsystem_menu();
}
if (Lab_weapon_fire_window != nullptr) {
Lab_weapon_fire_window->DeleteChildren();
labviewer_fill_fire_weapon_menu();
}
if (Lab_loadout_window != nullptr) {
Lab_loadout_window->DeleteChildren();
labviewer_fill_loadout_window();
}
if (Lab_trigger_animations_window != nullptr) {
Lab_trigger_animations_window->DeleteChildren();
labviewer_fill_animations_window();
}
}
// ----------------------------- Lab functions ---------------------------------
bool is_same_obj_type(int curr, int next)
{
if (Lab_mode == LAB_MODE_SHIP) {
if (Ship_info[curr].species == Ship_info[next].species) {
return true;
}
return false;
}
if (Lab_mode == LAB_MODE_WEAPON) {
if (Weapon_info[curr].subtype == Weapon_info[next].subtype) {
return true;
}
return false;
}
return false;
}
int lab_get_type_idx()
{
if (Lab_mode == LAB_MODE_SHIP) {
ship_info* si = &Ship_info[Lab_last_selected_object->GetData()];
return si->species;
} else if (Lab_mode == LAB_MODE_WEAPON) {
weapon_info* wi = &Weapon_info[Lab_last_selected_object->GetData()];
for (int i = 0; i < Num_weapon_subtypes; i++) {
if (i == wi->subtype) {
return i;
}
}
}
return -1;
}
// plieblang - scroll through entries with the arrow keys
void lab_scroll_up()
{
if (Lab_last_selected_object == nullptr) {
return;
}
// if we're at the beginning of the list, don't do anything
if (Lab_mode == LAB_MODE_SHIP) {
if (Lab_last_selected_object == ship_list_endpoints[lab_get_type_idx()].first) {
return;
}
} else if (Lab_mode == LAB_MODE_WEAPON) {
if (Lab_last_selected_object == weap_list_endpoints[lab_get_type_idx()].first) {
return;
}
}
int curr_obj_idx = Lab_last_selected_object->GetData();
int prev_obj_idx = ((TreeItem*)Lab_last_selected_object->prev)->GetData();
do {
Lab_last_selected_object = (TreeItem*)Lab_last_selected_object->prev;
curr_obj_idx = prev_obj_idx;
prev_obj_idx = Lab_last_selected_object->GetData();
} while (!is_same_obj_type(curr_obj_idx, prev_obj_idx));
if (Lab_mode == LAB_MODE_SHIP) {
ship_tree->SetSelectedItem(Lab_last_selected_object);
} else if (Lab_mode == LAB_MODE_WEAPON) {
weap_tree->SetSelectedItem(Lab_last_selected_object);
}
}
void lab_scroll_down()
{
if (Lab_last_selected_object == nullptr) {
return;
}
// if we're at the end of the list, don't do anything
if (Lab_mode == LAB_MODE_SHIP) {
if (Lab_last_selected_object == ship_list_endpoints[lab_get_type_idx()].second) {
return;
}
} else if (Lab_mode == LAB_MODE_WEAPON) {
if (Lab_last_selected_object == weap_list_endpoints[lab_get_type_idx()].second) {
return;
}
}
int curr_obj_idx = Lab_last_selected_object->GetData();
int next_obj_idx = ((TreeItem*)Lab_last_selected_object->next)->GetData();
do {
Lab_last_selected_object = (TreeItem*)Lab_last_selected_object->next;
curr_obj_idx = next_obj_idx;
next_obj_idx = Lab_last_selected_object->GetData();
} while (!is_same_obj_type(curr_obj_idx, next_obj_idx));
if (Lab_mode == LAB_MODE_SHIP) {
ship_tree->SetSelectedItem(Lab_last_selected_object);
} else if (Lab_mode == LAB_MODE_WEAPON) {
weap_tree->SetSelectedItem(Lab_last_selected_object);
}
}
// The lab simulates a mission environment; in order for all the various bits to work, we need to initialize a bunch of stuff that
// would normally be done during mission parse.
void lab_pseudomission_setup() {
// External weapon displays require a call to weapons_page_in, which in turn requires team data to be set
Num_teams = 1;
team_data* teamp = &Team_data[0];
// In the lab, all ships are valid
for (size_t i = 0; i < Ship_info.size(); ++i) {
teamp->ship_list[i] = static_cast<int>(i);
strcpy_s(teamp->ship_list_variables[i], "");
teamp->ship_count[i] = 1;
teamp->loadout_total += 1;
strcpy_s(teamp->ship_count_variables[i], "");
}
teamp->default_ship = 0;
teamp->num_ship_choices = ship_info_size();
// you want guns? you get guns.
for (size_t i = 0; i < Weapon_info.size(); ++i) {
teamp->weaponry_pool[i] = static_cast<int>(i);
teamp->weaponry_count[i] = 640; // should be enough for everyone
strcpy_s(teamp->weaponry_amount_variable[i], "");
strcpy_s(teamp->weaponry_pool_variable[i], "");
}
teamp->num_weapon_choices = weapon_info_size();
}
void lab_init()
{
GUIObject* cbp;
int x;
weapon_pause_sounds();
gr_set_clear_color(0, 0, 0);
// We start by creating the screen/toolbar
Lab_screen = GUI_system.PushScreen(new GUIScreen("Lab"));
Lab_toolbar = (Window*)Lab_screen->Add(new Window("Toolbar", gr_screen.center_offset_x, gr_screen.center_offset_y,
-1, -1, WS_NOTITLEBAR | WS_NONMOVEABLE));
// start filling the main toolbar
x = 0;
cbp = Lab_toolbar->AddChild(new Button("Ships", x, 0, labviewer_make_ship_window));
x += cbp->GetWidth() + 10;
cbp = Lab_toolbar->AddChild(new Button("Weapons", x, 0, labviewer_make_weap_window));
x += cbp->GetWidth() + 10;
cbp = Lab_toolbar->AddChild(new Button("Render Options", x, 0, labviewer_make_render_options_window));
x += cbp->GetWidth() + 10;
cbp = Lab_toolbar->AddChild(new Button("Material Overrides", x, 0, labviewer_make_material_override_window));
x += cbp->GetWidth() + 10;
cbp = Lab_toolbar->AddChild(new Button("Backgrounds", x, 0, labviewer_make_background_window));
x += cbp->GetWidth() + 10;
cbp = Lab_toolbar->AddChild(new Button("Actions", x, 0, labviewer_make_actions_window));
x += cbp->GetWidth() + 20;
cbp = Lab_toolbar->AddChild(new Button("Exit", x, 0, labviewer_exit));
// reset some defaults, just to be sure
Lab_model_pos = vmd_zero_vector;
Lab_mode = LAB_MODE_NONE;
Lab_thrust_len = 1.0f;
Lab_thrust_afterburn = false;
orig_cmdline_ambient = Cmdline_ambient_factor;
orig_cmdline_direct = static_light_factor;
orig_cmdline_bloom = Cmdline_bloom_intensity;
// save detail options
Lab_detail_texture_save = Detail.hardware_textures;
// load up the list of insignia that we might use on the ships
pilot_load_squad_pic_list();
// the default insignia bitmap
Lab_insignia_index = 0;
if (Num_pilot_squad_images > 0) {
Assert((Lab_insignia_index < Num_pilot_squad_images));
Lab_insignia_bitmap = bm_load_duplicate(Pilot_squad_image_names[Lab_insignia_index]);
}
// enable post-processing by default in the lab
PostProcessing_override = false;
// disable model rotation by default in the lab
Lab_viewer_flags |= LAB_FLAG_NO_ROTATION;
Lab_viewer_flags |= LAB_FLAG_INITIAL_ROTATION;
obj_init();
// Reset the background
labviewer_change_background_actual();
flagset<Object::Object_Flags> obs_flags;
auto obj_index = obj_create(OBJ_OBSERVER, -1, 0, &vmd_identity_matrix, &vmd_zero_vector, 0, obs_flags);
Player_obj = &Objects[obj_index];
Viewer_obj = Player_obj;
Viewer_mode = VM_EXTERNAL;
if (!Lab_cam.isValid())
Lab_cam = cam_create("Lab camera");
Lab_Save_Missiontime = Missiontime;
gr_init_alphacolor(&HUD_color_debug, 128, 255, 128, HUD_color_alpha * 16);
if (The_mission.ai_profile == nullptr)
The_mission.ai_profile = &Ai_profiles[Default_ai_profile];
lab_cam_distance = 100.0f;
lab_cam_phi = 1.24f;
lab_cam_theta = 2.25f;
Lab_selected_index = -1;
Lab_last_selected_ship = -1;
Lab_selected_object = -1;
Lab_last_selected_weapon = -1;
Lab_model_num = -1;
Lab_selected_mission = "None";
Lab_skybox_orientation = vmd_identity_matrix;
fireball_init();
debris_init();
extern void debris_page_in();
debris_page_in();
shockwave_level_init();
shipfx_flash_init();
mflash_page_in(true);
beam_level_init();
particle::init();
lab_pseudomission_setup();
ai_paused = 1;
Game_mode |= GM_LAB;
}
void labviewer_load_weapons() {
extern void weapons_page_in();
weapons_page_in();
Lab_weapons_loaded = true;
}
#include "lab.h"
#include "controlconfig/controlsconfig.h"
void lab_do_frame(float frametime)
{
GR_DEBUG_SCOPE("Lab Frame");
gr_reset_clip();
gr_clear();
Missiontime += Frametime;
labviewer_do_render(frametime);
bool test1 = (GUI_system.OnFrame(frametime, !(Trackball_active) ? true : false, false) == GSOF_NOTHINGPRESSED);
if (test1) {
int key = GUI_system.GetKeyPressed();
int status = GUI_system.GetStatus();
// set trackball modes
if (status & GST_MOUSE_LEFT_BUTTON) {
Trackball_active = 1;
Trackball_mode = 1; // rotate viewed object
if (key_get_shift_status() & KEY_SHIFTED) {
Trackball_mode = 2; // zoom
}
} else if (status & GST_MOUSE_RIGHT_BUTTON) {
Trackball_active = 1;
Trackball_mode = 3; // rotate camera
} else if (!mouse_down(MOUSE_LEFT_BUTTON | MOUSE_RIGHT_BUTTON)) {
// reset trackball modes
Trackball_active = 0;
Trackball_mode = 0;
}
// Due to switch scoping rules, this has to be declared here
SCP_map<SCP_string, team_color>::iterator color_itr = Team_Colors.find(Lab_team_color);
// handle any key presses
switch (key) {
// switch between the current insignia bitmap to render with
case KEY_DIVIDE: {
if (!(Lab_viewer_flags & LAB_FLAG_SHOW_INSIGNIA) || (Num_pilot_squad_images == 0)) {
break;
}
Lab_insignia_index = (Lab_insignia_index + 1) % Num_pilot_squad_images;
Assert((Lab_insignia_index >= 0) && (Lab_insignia_index < Num_pilot_squad_images));
if (Lab_insignia_bitmap >= 0) {
bm_release(Lab_insignia_bitmap);
Lab_insignia_bitmap = -1;
}
Lab_insignia_bitmap = bm_load_duplicate(Pilot_squad_image_names[Lab_insignia_index]);
break;
}
// change between damage lightning effects
case KEY_L:
// REIMPLEMENT
break;
// Adjust FXAA presets
case KEY_0:
if (!PostProcessing_override)
Gr_aa_mode = AntiAliasMode::FXAA_Low;
break;
case KEY_1:
if (!PostProcessing_override)
Gr_aa_mode = AntiAliasMode::FXAA_Medium;
break;
case KEY_2:
if (!PostProcessing_override)
Gr_aa_mode = AntiAliasMode::FXAA_High;
break;
case KEY_3:
if (!PostProcessing_override)
Gr_aa_mode = AntiAliasMode::SMAA_Low;
break;
case KEY_4:
if (!PostProcessing_override)
Gr_aa_mode = AntiAliasMode::SMAA_Medium;
break;
case KEY_5:
if (!PostProcessing_override)
Gr_aa_mode = AntiAliasMode::SMAA_High;
break;
case KEY_6:
if (!PostProcessing_override)
Gr_aa_mode = AntiAliasMode::SMAA_Ultra;
break;
case KEY_T:
if (color_itr == Team_Colors.begin()) {
color_itr = --Team_Colors.end();
Lab_team_color = color_itr->first;
} else {
--color_itr;
Lab_team_color = color_itr->first;
}
break;
case KEY_Y:
++color_itr;
if (color_itr == Team_Colors.end())
color_itr = Team_Colors.begin();
Lab_team_color = color_itr->first;
break;
case KEY_V:
reset_view();
break;
case KEY_UP:
lab_scroll_up();
break;
case KEY_DOWN:
lab_scroll_down();
break;
case KEY_R:
switch (Lab_rotation_mode) {
case Lab_rotation_modes::Both:
Lab_rotation_mode = Lab_rotation_modes::Yaw;
break;
case Lab_rotation_modes::Yaw:
Lab_rotation_mode = Lab_rotation_modes::Pitch;
break;
case Lab_rotation_modes::Pitch:
Lab_rotation_mode = Lab_rotation_modes::Roll;
break;
case Lab_rotation_modes::Roll:
Lab_rotation_mode = Lab_rotation_modes::Both;
break;
}
break;
case KEY_S:
Lab_manual_rotation_speed_divisor *= 10.f;
if (Lab_manual_rotation_speed_divisor > 10000.f)
Lab_manual_rotation_speed_divisor = 100.f;
break;
// bail...
case KEY_ESC:
labviewer_exit(nullptr);
break;
default: {
// check for game-specific controls
if (Lab_mode == LAB_MODE_SHIP) {
if (check_control(PLUS_5_PERCENT_THROTTLE, key))
Lab_thrust_len += 0.05f;
else if (check_control(MINUS_5_PERCENT_THROTTLE, key))
Lab_thrust_len -= 0.05f;
CLAMP(Lab_thrust_len, 0.0f, 1.0f);
if (check_control(AFTERBURNER, key))
Lab_thrust_afterburn = !Lab_thrust_afterburn;
}
break;
}
}
}
gr_flip();
}
void lab_close()
{
int i;
Lab_toolbar = nullptr;
Lab_class_toolbar = nullptr;
Lab_class_window = nullptr;
Lab_flags_window = nullptr;
Lab_render_options_window = nullptr;
Lab_background_window = nullptr;
delete Lab_screen;
Lab_screen = nullptr;
if (Lab_species_nodes != nullptr) {
for (i = 0; i < (int)Species_info.size(); i++) {
delete Lab_species_nodes[i];
}
delete[] Lab_species_nodes;
Lab_species_nodes = nullptr;
}
if (Lab_insignia_bitmap >= 0) {
bm_release(Lab_insignia_bitmap);
Lab_insignia_bitmap = -1;
}
Lab_insignia_index = -1;
if (Lab_model_num != -1) {
model_page_out_textures(Lab_model_num, true);
model_unload(Lab_model_num);
Lab_model_num = -1;
}
Lab_selected_mission = "None";
stars_pre_level_init(true);
memset(Lab_model_filename, 0, sizeof(Lab_model_filename));
Basemap_override = false;
Envmap_override = false;
Specmap_override = false;
Normalmap_override = false;
Heightmap_override = false;
Glowpoint_override = false;
PostProcessing_override = false;
Lab_Basemap_override = false;
Lab_Glowmap_override = false;
Lab_Specmap_override = false;
Lab_Envmap_override = false;
Lab_Normalmap_override = false;
Lab_Heightmap_override = false;
// reset detail levels to default
Detail.hardware_textures = Lab_detail_texture_save;
// Reset ambient factor
gr_calculate_ambient_factor(Cmdline_ambient_factor);
weapon_unpause_sounds();
// audiostream_unpause_all();
game_flush();
cam_delete(Lab_cam);
obj_init();
shockwave_level_close();
beam_level_close();
ai_paused = 0;
physics_paused = 0;
Game_mode &= ~GM_LAB;
}