Files
fs2open.github.com/code/menuui/techmenu.cpp
Goober5000 67b3c6ce46 fix use of default tech database flags
The "default" tech database flags indicate whether entries are in the tech room by default, that is when the table is originally parsed and the campaign is started.  Flags may be changed later, so that when the tech database is "reset to default" the flags can be returned to the known state as specified in the tables.

A bug was introduced back in commit c924e3997a (!) based on a misinterpretation of what "default" meant.  This fixes the bug and clarifies the flags.

Fixes #4390.
2022-06-11 18:03:21 -04:00

1399 lines
40 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 "anim/animplay.h"
#include "anim/packunpack.h"
#include "cmdline/cmdline.h"
#include "gamehelp/contexthelp.h"
#include "gamesequence/gamesequence.h"
#include "gamesnd/gamesnd.h"
#include "globalincs/alphacolors.h"
#include "graphics/font.h"
#include "graphics/shadows.h"
#include "graphics/matrix.h"
#include "io/key.h"
#include "io/mouse.h"
#include "lighting/lighting.h"
#include "localization/localize.h"
#include "menuui/techmenu.h"
#include "missionui/missionscreencommon.h"
#include "parse/parselo.h"
#include "playerman/player.h"
#include "popup/popup.h"
#include "render/3d.h"
#include "render/batching.h"
#include "ship/ship.h"
#include "sound/fsspeech.h"
#include "ui/ui.h"
#include "ui/uidefs.h"
#include "weapon/weapon.h"
#define REVOLUTION_RATE 5.2f
#define NUM_BUTTONS 16
#define NUM_TABS 3
#define LIST_BUTTONS_MAX 50
#define SHIPS_DATA_MODE (1<<0)
#define WEAPONS_DATA_MODE (1<<1)
#define SPECIES_DATA_MODE (1<<2)
#define WEAPONS_SPECIES_DATA_MODE (WEAPONS_DATA_MODE | SPECIES_DATA_MODE)
#define SHIPS_DATA_TAB 0
#define WEAPONS_DATA_TAB 1
#define INTEL_DATA_TAB 2
#define TECH_DATABASE_TAB 3
#define SIMULATOR_TAB 4
#define CUTSCENES_TAB 5
#define CREDITS_TAB 6
#define SCROLL_LIST_UP 7
#define SCROLL_LIST_DOWN 8
#define SCROLL_INFO_UP 9
#define SCROLL_INFO_DOWN 10
#define PREV_ENTRY_BUTTON 11
#define NEXT_ENTRY_BUTTON 12
#define HELP_BUTTON 13
#define OPTIONS_BUTTON 14
#define EXIT_BUTTON 15
#define REPEAT (1<<0)
#define NO_MOUSE_OVER_SOUND (1<<1)
// indicies for coords
#define SHIP_X_COORD 0
#define SHIP_Y_COORD 1
#define SHIP_W_COORD 2
#define SHIP_H_COORD 3
// background filename for species
// note weapon filename is now same as ship filename
const char *Tech_background_filename[GR_NUM_RESOLUTIONS] = {
"TechShipData",
"2_TechShipData"
};
const char *Tech_mask_filename[GR_NUM_RESOLUTIONS] = {
"TechShipData-M",
"2_TechShipData-M"
};
const char *Tech_slider_filename[GR_NUM_RESOLUTIONS] = {
"slider",
"2_slider"
};
int Tech_list_coords[GR_NUM_RESOLUTIONS][4] = {
{ // GR_640
27, 98, 161, 234
},
{ // GR_1024
43, 157, 253, 374
}
};
int Tech_ship_display_coords[GR_NUM_RESOLUTIONS][4] = {
{ // GR_640
239, 98, 393, 222
},
{ // GR_1024
382, 158, 629, 355
}
};
int Tech_desc_coords[GR_NUM_RESOLUTIONS][4] = {
{ // GR_640
29, 347, 365, 125
},
{ // GR_1024
47, 555, 584, 200
}
};
int Tech_ani_centre_coords[GR_NUM_RESOLUTIONS][2] = {
{ // GR_640
416, 215
},
{ // GR_1024
669, 345
}
};
int Tech_slider_coords[GR_NUM_RESOLUTIONS][4] = {
{ // GR_640
2, 118, 20, 194
},
{ // GR_1024
3, 190, 32, 310
}
};
#define MAX_TEXT_LINES 150
#define MAX_TEXT_LINE_LEN 256
struct techroom_buttons {
const char *filename;
int x, y, xt, yt;
int hotspot;
int tab;
int flags;
UI_BUTTON button; // because we have a class inside this struct, we need the constructor below..
techroom_buttons(const char *name, int x1, int y1, int xt1, int yt1, int h, int t, int f = 0) : filename(name), x(x1), y(y1), xt(xt1), yt(yt1), hotspot(h), tab(t), flags(f) {}
};
static techroom_buttons Buttons[GR_NUM_RESOLUTIONS][NUM_BUTTONS] = {
{ // GR_640
techroom_buttons("TDB_04", 406, 384, 447, 393, 4, -1), // ship data tab
techroom_buttons("TDB_05", 404, 418, 447, 429, 5, -1), // weapons data tab
techroom_buttons("TDB_06", 404, 447, 447, 461, 6, -1), // species data tab
techroom_buttons("TDB_00", 7, 3, 37, 7, 0, -1), // technical database tab
techroom_buttons("TDB_01", 7, 18, 37, 23, 1, -1), // mission simulator tab
techroom_buttons("TDB_02", 7, 34, 37, 38, 2, -1), // cutscenes tab
techroom_buttons("TDB_03", 7, 49, 37, 54, 3, -1), // credits tab
techroom_buttons("TDB_07", 1, 86, -1, -1, 7, SHIPS_DATA_MODE, REPEAT), // prev data entry
techroom_buttons("TDB_08", 1, 317, -1, -1, 8, SHIPS_DATA_MODE, REPEAT), // next data entry
techroom_buttons("TDB_09", 1, 406, -1, -1, 9, SHIPS_DATA_MODE, REPEAT), // prev data entry
techroom_buttons("TDB_10", 1, 447, -1, -1, 10, SHIPS_DATA_MODE, REPEAT), // next data entry
techroom_buttons("TDB_11a",559, 323, -1, -1, 11, SHIPS_DATA_MODE, REPEAT), // prev data entry
techroom_buttons("TDB_12a",609, 323, -1, -1, 12, SHIPS_DATA_MODE, REPEAT), // next data entry
techroom_buttons("TDB_13", 533, 425, 500, 440, 13, -1), // help
techroom_buttons("TDB_14", 533, 455, 479, 464, 14, -1), // options
techroom_buttons("TDB_15a",571, 425, 588, 413, 15, -1), // exit
},
{ // GR_1024
techroom_buttons("2_TDB_04", 649, 614, 717, 630, 4, -1), // ship data tab
techroom_buttons("2_TDB_05", 646, 669, 717, 687, 5, -1), // weapons data tab
techroom_buttons("2_TDB_06", 646, 716, 717, 739, 6, -1), // species data tab
techroom_buttons("2_TDB_00", 12, 5, 59, 12, 0, -1), // technical database tab
techroom_buttons("2_TDB_01", 12, 31, 59, 37, 1, -1), // mission simulator tab
techroom_buttons("2_TDB_02", 12, 56, 59, 62, 2, -1), // cutscenes tab
techroom_buttons("2_TDB_03", 12, 81, 59, 88, 3, -1), // credits tab
techroom_buttons("2_TDB_07", 1, 138, -1, -1, 7, SHIPS_DATA_MODE, REPEAT), // prev data entry
techroom_buttons("2_TDB_08", 1, 507, -1, -1, 8, SHIPS_DATA_MODE, REPEAT), // next data entry
techroom_buttons("2_TDB_09", 1, 649, -1, -1, 9, SHIPS_DATA_MODE, REPEAT), // prev data entry
techroom_buttons("2_TDB_10", 1, 716, -1, -1, 10, SHIPS_DATA_MODE, REPEAT), // next data entry
techroom_buttons("2_TDB_11a", 895, 518, -1, -1, 11, SHIPS_DATA_MODE, REPEAT), // prev data entry
techroom_buttons("2_TDB_12a", 974, 518, -1, -1, 12, SHIPS_DATA_MODE, REPEAT), // next data entry
techroom_buttons("2_TDB_13", 854, 681, 800, 704, 13, -1), // help
techroom_buttons("2_TDB_14", 854, 728, 780, 743, 14, -1), // options
techroom_buttons("2_TDB_15a", 914, 681, 930, 660, 15, -1), // exit
},
};
static UI_WINDOW Ui_window;
static UI_BUTTON View_window;
static int Tech_background_bitmap;
static int Tech_background_bitmap_mask;
static int Tab = SHIPS_DATA_TAB;
static int List_offset;
static int Select_tease_line;
static int Trackball_mode = 1;
static int Trackball_active = 0;
static matrix Techroom_ship_orient = IDENTITY_MATRIX;
static int Techroom_show_all = 0;
static int Text_size;
static int Text_offset;
static int Text_line_size[MAX_TEXT_LINES];
static const char *Text_lines[MAX_TEXT_LINES];
static int Cur_entry = -1; // this is the current entry selected, using entry indexing
static int Cur_entry_index = -1; // this is the current entry selected, using master list indexing
static int Techroom_ship_modelnum = -1;
static int Techroom_ship_model_instance = -1;
static float Techroom_ship_rot;
static UI_BUTTON List_buttons[LIST_BUTTONS_MAX]; // buttons for each line of text in list
static bool Ships_loaded = false;
static bool Weapons_loaded = false;
static bool Intel_loaded = false;
int Techroom_overlay_id;
// out entry data struct & vars
typedef struct {
int index; // index into the master table that its in (ie Ship_info[])
const char* name; // ptr to name string
const char* desc; // ptr to description string
char tech_anim_filename[MAX_FILENAME_LEN]; //duh
generic_anim animation; // animation info
int bitmap; // bitmap handle
int has_anim; // flag to indicate the presence of an animation for this item
int model_num; // model reference handle
int textures_loaded; // if the model has textures loaded for it or not (hacky mem management)
} tech_list_entry;
static SCP_vector<tech_list_entry> Ship_list;
static SCP_vector<tech_list_entry> Weapon_list;
static SCP_vector<tech_list_entry> Intel_list;
static SCP_vector<tech_list_entry>* Current_list = &Ship_list; // A pointer to the current display list
// slider stuff
static UI_SLIDER2 Tech_slider;
// Intelligence master data structs (these get inited @ game startup from species.tbl)
SCP_vector<intel_data> Intel_info;
bool Intel_inited = false;
// some prototypes to make you happy
int techroom_load_ani(anim **animpp, char *name);
void tech_common_render();
void tech_scroll_list_up();
void tech_scroll_list_down();
////////////////////////////////////////////////////
// like, functions and stuff
void techroom_init_desc(const char *src, int w)
{
Text_size = Text_offset = 0;
if (!src) {
return;
}
Text_size = split_str(src, w, Text_line_size, Text_lines, MAX_TEXT_LINES, MAX_TEXT_LINE_LEN);
Assert(Text_size >= 0 && Text_size < MAX_TEXT_LINES);
}
void techroom_unload_animation()
{
//clear everything, just in case, it will get loaded when needed later
for (auto& list_entry : Weapon_list) {
if (list_entry.animation.type != BM_TYPE_NONE && list_entry.has_anim != 0) {
generic_anim_unload(&list_entry.animation);
}
if (list_entry.bitmap >= 0) {
bm_release(list_entry.bitmap);
list_entry.bitmap = -1;
}
}
for (auto & intel_entry : Intel_list) {
if (intel_entry.animation.type != BM_TYPE_NONE && intel_entry.has_anim != 0) {
generic_anim_unload(&intel_entry.animation);
}
if (intel_entry.bitmap >= 0) {
bm_release(intel_entry.bitmap);
intel_entry.bitmap = -1;
}
}
}
void techroom_select_new_entry()
{
if (Current_list->empty()) {
Cur_entry_index = Cur_entry = -1;
techroom_init_desc(nullptr,0);
return;
}
Assert(Cur_entry < static_cast<int>(Current_list->size()));
Cur_entry_index = Current_list->at(Cur_entry).index;
Assert( Cur_entry_index >= 0 );
// if we are in the ships tab, load the ship model
if (Tab == SHIPS_DATA_TAB) {
ship_info *sip = &Ship_info[Cur_entry_index];
int i = 0;
// little memory management, kinda hacky but it should keep the techroom at around
// 100meg rather than the 700+ it can get to with all ships loaded - taylor
for (auto & list_entry : *Current_list) {
if ((list_entry.model_num > -1) && (list_entry.textures_loaded)) {
// don't unload any spot within 5 of current
if ((i < Cur_entry + 5) && (i > Cur_entry - 5) )
continue;
mprintf(("TECH ROOM: Dumping excess ship textures...\n"));
model_page_out_textures(list_entry.model_num);
list_entry.textures_loaded = 0;
}
i++;
}
Techroom_ship_modelnum = model_load(sip->pof_file, sip->n_subsystems, &sip->subsystems[0]);
if (Techroom_ship_model_instance >= 0) {
model_delete_instance(Techroom_ship_model_instance);
}
Techroom_ship_model_instance = model_create_instance(-1, Techroom_ship_modelnum);
model_set_up_techroom_instance(sip, Techroom_ship_model_instance);
Current_list->at(Cur_entry).model_num = Techroom_ship_modelnum;
// page in ship textures properly (takes care of nondimming pixels)
model_page_in_textures(Techroom_ship_modelnum, Cur_entry_index);
Current_list->at(Cur_entry).textures_loaded = 1;
} else {
Techroom_ship_modelnum = -1;
if (Techroom_ship_model_instance >= 0) {
model_delete_instance(Techroom_ship_model_instance);
Techroom_ship_model_instance = -1;
}
Trackball_mode = 0;
// load animation here, we now only have one loaded
int stream_result = generic_anim_init_and_stream(&Current_list->at(Cur_entry).animation, Current_list->at(Cur_entry).tech_anim_filename, bm_get_type(Tech_background_bitmap), true);
if (stream_result >= 0) {
Current_list->at(Cur_entry).has_anim = 1;
} else {
// we've failed to load any animation
// load an image and treat it like a 1 frame animation
Current_list->at(Cur_entry).bitmap = bm_load(Current_list->at(Cur_entry).tech_anim_filename);
}
}
techroom_init_desc(Current_list->at(Cur_entry).desc, Tech_desc_coords[gr_screen.res][SHIP_W_COORD]);
fsspeech_play(FSSPEECH_FROM_TECHROOM, Current_list->at(Cur_entry).desc);
}
// write out the current description in the bottom window
void techroom_render_desc(int xo, int yo, int ho)
{
int y, z, len, font_height;
char line[MAX_TEXT_LINE_LEN + 1];
font_height = gr_get_font_height();
y = 0;
z = Text_offset;
while (y + font_height <= ho) {
if (z >= Text_size){
break;
}
len = Text_line_size[z];
if (len > MAX_TEXT_LINE_LEN){
len = MAX_TEXT_LINE_LEN;
}
strncpy(line, Text_lines[z], len);
line[len] = 0;
gr_string(xo, yo + y, line, GR_RESIZE_MENU);
y += font_height;
z++;
}
// maybe output 'more' indicator
if ( z < Text_size ) {
// can be scrolled down
int more_txt_x = Tech_desc_coords[gr_screen.res][0] + (Tech_desc_coords[gr_screen.res][2]/2) - 10; // FIXME should move these to constants since they don't move
int more_txt_y = Tech_desc_coords[gr_screen.res][1] + Tech_desc_coords[gr_screen.res][3]; // located below brief text, centered
int w, h;
gr_get_string_size(&w, &h, XSTR("more", 1469), static_cast<int>(strlen(XSTR("more", 1469))));
gr_set_color_fast(&Color_black);
gr_rect(more_txt_x-2, more_txt_y, w+3, h, GR_RESIZE_MENU);
gr_set_color_fast(&Color_more_indicator);
gr_string(more_txt_x, more_txt_y, XSTR("more", 1469), GR_RESIZE_MENU); // base location on the input x and y?
}
}
// renders the stuff common to all 3 tech room tabs
void tech_common_render()
{
char buf[256];
int y, z, font_height;
// render description in its box
gr_set_color_fast(&Color_text_normal);
techroom_render_desc(Tech_desc_coords[gr_screen.res][SHIP_X_COORD], Tech_desc_coords[gr_screen.res][SHIP_Y_COORD], Tech_desc_coords[gr_screen.res][SHIP_H_COORD]);
font_height = gr_get_font_height();
// draw the list of entries
y = 0;
z = List_offset;
while (y + font_height <= Tech_list_coords[gr_screen.res][SHIP_H_COORD]) {
if ((z - List_offset) >= LIST_BUTTONS_MAX || z >= static_cast<int>(Current_list->size())) {
break;
}
if (z == Cur_entry) {
gr_set_color_fast(&Color_text_selected);
} else if (z == Select_tease_line) {
gr_set_color_fast(&Color_text_subselected);
} else {
gr_set_color_fast(&Color_text_normal);
}
memset( buf, 0, sizeof(buf) );
strncpy(buf, Current_list->at(z).name, sizeof(buf) - 1);
if (Lcl_gr && !Disable_built_in_translations)
lcl_translate_ship_name_gr(buf);
font::force_fit_string(buf, 255, Tech_list_coords[gr_screen.res][SHIP_W_COORD]);
gr_string(Tech_list_coords[gr_screen.res][SHIP_X_COORD], Tech_list_coords[gr_screen.res][SHIP_Y_COORD] + y, buf, GR_RESIZE_MENU);
List_buttons[z - List_offset].update_dimensions(Tech_list_coords[gr_screen.res][SHIP_X_COORD], Tech_list_coords[gr_screen.res][SHIP_Y_COORD] + y, Tech_list_coords[gr_screen.res][SHIP_W_COORD], font_height);
List_buttons[z - List_offset].enable(1);
y += font_height;
z++;
}
// disable the rest of the list buttons
z -= List_offset;
while (z < LIST_BUTTONS_MAX) {
List_buttons[z++].disable();
}
}
void techroom_ships_render(float frametime)
{
// render all the common stuff
tech_common_render();
if(Cur_entry_index == -1)
return;
// now render the trackball ship, which is unique to the ships tab
float rev_rate = REVOLUTION_RATE;
angles rot_angles, view_angles;
ship_info *sip = &Ship_info[Cur_entry_index];
model_render_params render_info;
if (sip->uses_team_colors) {
render_info.set_team_color(sip->default_team_name, "none", 0, 0);
}
// get correct revolution rate
if (sip->is_big_ship()) {
rev_rate *= 1.7f;
}
if (sip->is_huge_ship()) {
rev_rate *= 3.0f;
}
// rotate the ship as much as required for this frame
Techroom_ship_rot += PI2 * frametime / rev_rate;
while (Techroom_ship_rot > PI2){
Techroom_ship_rot -= PI2;
}
// reorient ship
if (Trackball_active) {
int dx, dy;
matrix mat1, mat2;
if (Trackball_active) {
mouse_get_delta(&dx, &dy);
if (dx || dy) {
vm_trackball(-dx, -dy, &mat1);
vm_matrix_x_matrix(&mat2, &mat1, &Techroom_ship_orient);
Techroom_ship_orient = mat2;
}
}
} else {
// setup stuff needed to render the ship
view_angles.p = -0.6f;
view_angles.b = 0.0f;
view_angles.h = 0.0f;
vm_angles_2_matrix(&Techroom_ship_orient, &view_angles);
rot_angles.p = 0.0f;
rot_angles.b = 0.0f;
rot_angles.h = Techroom_ship_rot;
vm_rotate_matrix_by_angles(&Techroom_ship_orient, &rot_angles);
}
gr_set_clip(Tech_ship_display_coords[gr_screen.res][SHIP_X_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_Y_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_W_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_H_COORD], GR_RESIZE_MENU);
// render the ship
g3_start_frame(1);
g3_set_view_matrix(&sip->closeup_pos, &vmd_identity_matrix, sip->closeup_zoom * 1.3f);
// lighting for techroom
light_reset();
vec3d light_dir = vmd_zero_vector;
light_dir.xyz.y = 1.0f;
light_dir.xyz.x = 0.0000001f;
light_add_directional(&light_dir, 0.85f, 1.0f, 1.0f, 1.0f);
light_rotate_all();
// lighting for techroom
Glowpoint_use_depth_buffer = false;
model_clear_instance(Techroom_ship_modelnum);
render_info.set_detail_level_lock(0);
if (sip->replacement_textures.size() > 0)
{
render_info.set_replacement_textures(Techroom_ship_modelnum, sip->replacement_textures);
}
if(Shadow_quality != ShadowQuality::Disabled)
{
gr_reset_clip();
auto pm = model_get(Techroom_ship_modelnum);
shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, gr_screen.clip_aspect, -sip->closeup_pos.xyz.z + pm->rad, -sip->closeup_pos.xyz.z + pm->rad + 200.0f, -sip->closeup_pos.xyz.z + pm->rad + 2000.0f, -sip->closeup_pos.xyz.z + pm->rad + 10000.0f);
render_info.set_flags(MR_NO_TEXTURING | MR_NO_LIGHTING | MR_AUTOCENTER);
model_render_immediate(&render_info, Techroom_ship_modelnum, Techroom_ship_model_instance, &Techroom_ship_orient, &vmd_zero_vector);
shadows_end_render();
gr_set_clip(Tech_ship_display_coords[gr_screen.res][SHIP_X_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_Y_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_W_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_H_COORD], GR_RESIZE_MENU);
}
gr_set_proj_matrix(Proj_fov, gr_screen.clip_aspect, Min_draw_distance, Max_draw_distance);
gr_set_view_matrix(&Eye_position, &Eye_matrix);
uint render_flags = MR_AUTOCENTER;
if(sip->flags[Ship::Info_Flags::No_lighting])
render_flags |= MR_NO_LIGHTING;
render_info.set_flags(render_flags);
model_render_immediate(&render_info, Techroom_ship_modelnum, Techroom_ship_model_instance, &Techroom_ship_orient, &vmd_zero_vector);
Glowpoint_use_depth_buffer = true;
batching_render_all();
gr_end_view_matrix();
gr_end_proj_matrix();
g3_end_frame();
gr_reset_clip();
}
// select previous entry in current list
void tech_prev_entry()
{
//unload the current animation, we load another one for the new current entry
techroom_unload_animation();
Cur_entry--;
if (Cur_entry < 0) {
Cur_entry = static_cast<int>(Current_list->size() - 1);
// scroll to end of list
List_offset = Cur_entry - Tech_list_coords[gr_screen.res][SHIP_H_COORD] / gr_get_font_height() + 1;
if (List_offset < 0) {
// this happens when there are not enough items to scroll
List_offset = 0;
}
Tech_slider.force_currentItem(Tech_slider.get_numberItems());
} else {
// maybe adjust list position by 1
if (List_offset > Cur_entry) {
tech_scroll_list_up();
Tech_slider.forceUp();
}
}
techroom_select_new_entry();
gamesnd_play_iface(InterfaceSounds::SCROLL);
}
// select next entry in current list
void tech_next_entry()
{
//unload the current animation, we load another one for the new current entry
techroom_unload_animation();
Cur_entry++;
if (Cur_entry >= static_cast<int>(Current_list->size())) {
Cur_entry = 0;
// scroll to beginning of list
List_offset = 0;
Tech_slider.force_currentItem(Cur_entry);
} else {
// maybe adjust list position by 1
if (List_offset + Tech_list_coords[gr_screen.res][SHIP_H_COORD] / gr_get_font_height() <= Cur_entry) {
tech_scroll_list_down();
Tech_slider.forceDown();
}
}
techroom_select_new_entry();
gamesnd_play_iface(InterfaceSounds::SCROLL);
}
void tech_scroll_info_up()
{
if (Text_offset) {
Text_offset--;
gamesnd_play_iface(InterfaceSounds::SCROLL);
} else {
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
}
}
void tech_scroll_info_down()
{
int h;
h = Tech_desc_coords[gr_screen.res][SHIP_H_COORD];
if (Text_offset + h / gr_get_font_height() < Text_size) {
Text_offset++;
gamesnd_play_iface(InterfaceSounds::SCROLL);
} else { //-V523
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
}
}
void tech_scroll_list_up()
{
//int last;
if (List_offset > 0) {
List_offset--;
gamesnd_play_iface(InterfaceSounds::SCROLL);
} else {
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
}
}
void tech_scroll_list_down()
{
if (List_offset + Tech_list_coords[gr_screen.res][SHIP_H_COORD] / gr_get_font_height() < static_cast<int>(Current_list->size())) {
List_offset++;
gamesnd_play_iface(InterfaceSounds::SCROLL);
} else {
gamesnd_play_iface(InterfaceSounds::GENERAL_FAIL);
}
}
// this doesn't do a thing...
void tech_ship_scroll_capture()
{
//unload the current animation, we load another one for the new current entry
techroom_unload_animation();
techroom_select_new_entry();
}
void techroom_anim_render(float frametime)
{
int x, y;
// render common stuff
tech_common_render();
// exit now if there are no entries to show
if (Current_list->empty() || Cur_entry < 0 || Cur_entry >= static_cast<int>(Current_list->size()))
return;
// render the animation
if(Current_list->at(Cur_entry).animation.num_frames > 0)
{
//grab dimensions
bm_get_info((Current_list->at(Cur_entry).animation.streaming) ? Current_list->at(Cur_entry).animation.bitmap_id : Current_list->at(Cur_entry).animation.first_frame, &x, &y, nullptr, nullptr, nullptr);
//get the centre point - adjust
x = Tech_ani_centre_coords[gr_screen.res][0] - x / 2;
y = Tech_ani_centre_coords[gr_screen.res][1] - y / 2;
generic_anim_render(&Current_list->at(Cur_entry).animation, frametime, x, y, true);
}
// if our active item has a bitmap instead of an animation, draw it
else if((Cur_entry >= 0) && (Current_list->at(Cur_entry).bitmap >= 0)){
//grab dimensions
bm_get_info(Current_list->at(Cur_entry).bitmap, &x, &y, nullptr, nullptr, nullptr);
//get the centre point - adjust
x = Tech_ani_centre_coords[gr_screen.res][0] - x / 2;
y = Tech_ani_centre_coords[gr_screen.res][1] - y / 2;
gr_set_bitmap(Current_list->at(Cur_entry).bitmap);
gr_bitmap(x, y, GR_RESIZE_MENU);
}
}
void techroom_change_tab(int num)
{
int multi = 0, font_height, max_num_entries_viewable;
flagset<Weapon::Info_Flags> wi_mask;
flagset<Ship::Info_Flags> si_mask;
//unload the current animation, we load another one for the new current entry
if(Tab != SHIPS_DATA_TAB)
techroom_unload_animation();
Tab = num;
List_offset = 0;
Cur_entry = 0;
multi = Player->flags & PLAYER_FLAGS_IS_MULTI;
for (int i=0; i<LIST_BUTTONS_MAX; i++){
List_buttons[i].disable();
}
// disable some stuff in multiplayer
if (Player->flags & PLAYER_FLAGS_IS_MULTI) {
Buttons[gr_screen.res][SIMULATOR_TAB].button.disable();
Buttons[gr_screen.res][CUTSCENES_TAB].button.disable();
}
switch (Tab) {
case SHIPS_DATA_TAB:
si_mask.set(multi ? Ship::Info_Flags::In_tech_database_m : Ship::Info_Flags::In_tech_database);
// load ship info if necessary
if ( !Ships_loaded ) {
if (Ship_list.empty()) {
Ship_list.reserve(Ship_info.size());
}
tech_list_entry temp_entry;
// we always initially set these values, so keep them outside the loop.
temp_entry.bitmap = -1;
temp_entry.animation.num_frames = 0; // no anim for ships
temp_entry.has_anim = 0; // no anim for ships
temp_entry.model_num = -1;
temp_entry.textures_loaded = 0;
for (auto it = Ship_info.begin(); it != Ship_info.end(); ++it)
{
if (Techroom_show_all || (it->flags & si_mask).any_set())
{
// this ship should be displayed, fill out the entry struct
temp_entry.index = (int)std::distance(Ship_info.begin(), it);
temp_entry.name = *it->tech_title ? it->tech_title : it->get_display_name();
temp_entry.desc = it->tech_desc;
Ship_list.push_back(temp_entry);
}
}
Ships_loaded = true;
}
Current_list = &Ship_list;
font_height = gr_get_font_height();
max_num_entries_viewable = Tech_list_coords[gr_screen.res][SHIP_H_COORD] / font_height;
Tech_slider.set_numberItems((int)Current_list->size() > max_num_entries_viewable ? (int)Current_list->size()-max_num_entries_viewable : 0);
// no anim to start here
break;
case WEAPONS_DATA_TAB:
// load weapon info & anims if necessary
if ( !Weapons_loaded ) {
Weapon_list.reserve(Weapon_info.size());
wi_mask.set(multi ? Weapon::Info_Flags::Player_allowed : Weapon::Info_Flags::In_tech_database);
int i = 0;
tech_list_entry temp_entry;
// we always initially set these values, so keep them outside the loop.
temp_entry.has_anim = 1;
temp_entry.bitmap = -1;
temp_entry.animation.num_frames = 0;
temp_entry.model_num = -1;
temp_entry.textures_loaded = 0;
for (auto &wi : Weapon_info)
{
if (Techroom_show_all || (wi.wi_flags & wi_mask).any_set())
{
// we have a weapon that should be in the tech db, so fill out specific info
temp_entry.index = i;
temp_entry.desc = wi.tech_desc;
temp_entry.name = wi.tech_title[0] ? wi.tech_title : wi.get_display_name();
// copy the weapon animation filename
strncpy(temp_entry.tech_anim_filename, wi.tech_anim_filename, MAX_FILENAME_LEN - 1);
Weapon_list.push_back(temp_entry);
}
++i;
}
Weapons_loaded = true;
}
Current_list = &Weapon_list;
font_height = gr_get_font_height();
max_num_entries_viewable = Tech_list_coords[gr_screen.res][SHIP_H_COORD] / font_height;
Tech_slider.set_numberItems(static_cast<int>(Current_list->size()) > max_num_entries_viewable ? static_cast<int>(Current_list->size())-max_num_entries_viewable : 0);
break;
case INTEL_DATA_TAB:
// load intel if necessary
if ( !Intel_loaded ) {
if (Intel_list.empty()) {
Intel_list.reserve(Intel_info.size());
}
int i = 0;
tech_list_entry temp_entry;
// we always initially set these values, so keep them outside the loop.
temp_entry.has_anim = 0;
temp_entry.model_num = -1;
temp_entry.textures_loaded = 0;
for (auto &ii : Intel_info) {
if (Techroom_show_all || (ii.flags & IIF_IN_TECH_DATABASE)) {
// leave option for no animation if string == "none"
if (!strcmp(ii.anim_filename, "none")) {
temp_entry.animation.num_frames = 0;
} else {
// try and load as an animation
temp_entry.bitmap = -1;
strncpy(temp_entry.tech_anim_filename, ii.anim_filename, NAME_LENGTH - 1);
}
temp_entry.desc = ii.desc.c_str();
temp_entry.index = i;
temp_entry.name = ii.name;
Intel_list.push_back(temp_entry);
}
++i;
}
Intel_loaded = true;
}
Current_list = &Intel_list;
font_height = gr_get_font_height();
max_num_entries_viewable = Tech_list_coords[gr_screen.res][SHIP_H_COORD] / font_height;
Tech_slider.set_numberItems(static_cast<int>(Current_list->size()) > max_num_entries_viewable ? static_cast<int>(Current_list->size())-max_num_entries_viewable : 0);
break;
}
// reset the entry
Cur_entry = 0;
techroom_select_new_entry();
}
int techroom_button_pressed(int num)
{
switch (num) {
case SHIPS_DATA_TAB:
case WEAPONS_DATA_TAB:
case INTEL_DATA_TAB:
fsspeech_stop();
techroom_change_tab(num);
break;
case SIMULATOR_TAB:
fsspeech_stop();
gamesnd_play_iface(InterfaceSounds::SWITCH_SCREENS);
gameseq_post_event(GS_EVENT_SIMULATOR_ROOM);
return 1;
case CUTSCENES_TAB:
fsspeech_stop();
gamesnd_play_iface(InterfaceSounds::SWITCH_SCREENS);
gameseq_post_event(GS_EVENT_GOTO_VIEW_CUTSCENES_SCREEN);
return 1;
case CREDITS_TAB:
fsspeech_stop();
gamesnd_play_iface(InterfaceSounds::SWITCH_SCREENS);
gameseq_post_event(GS_EVENT_CREDITS);
return 1;
case PREV_ENTRY_BUTTON:
tech_prev_entry();
break;
case NEXT_ENTRY_BUTTON:
tech_next_entry();
break;
case SCROLL_LIST_UP:
tech_scroll_list_up();
Tech_slider.forceUp();
break;
case SCROLL_LIST_DOWN:
tech_scroll_list_down();
Tech_slider.forceDown();
break;
case SCROLL_INFO_UP:
tech_scroll_info_up();
break;
case SCROLL_INFO_DOWN:
tech_scroll_info_down();
break;
case HELP_BUTTON:
launch_context_help();
gamesnd_play_iface(InterfaceSounds::HELP_PRESSED);
break;
case OPTIONS_BUTTON:
gamesnd_play_iface(InterfaceSounds::SWITCH_SCREENS);
gameseq_post_event(GS_EVENT_OPTIONS_MENU);
break;
case EXIT_BUTTON:
fsspeech_stop();
gamesnd_play_iface(InterfaceSounds::COMMIT_PRESSED);
gameseq_post_event(GS_EVENT_MAIN_MENU);
break;
}
return 0;
}
int techroom_load_ani(anim ** /*animpp*/, char *name)
{
int load_attempts = 0;
char anim_filename[64] = "2_";
// hi-res support
// (i don't think there are any hi-res anims for these tho)
if (gr_screen.res == GR_1024) {
strcat_s(anim_filename, name);
} else {
strcpy_s(anim_filename, name);
}
while(1) {
if ( load_attempts++ > 5 ) {
return 0;
}
return 1;
}
// bogus
return 0;
}
void techroom_intel_init()
{
int temp;
if (Intel_inited)
return;
try
{
read_file_text("species.tbl", CF_TYPE_TABLES);
reset_parse();
Intel_info.clear();
while (optional_string("$Entry:")) {
intel_data entry;
entry.flags = IIF_DEFAULT_VALUE;
required_string("$Name:");
stuff_string(entry.name, F_NAME, NAME_LENGTH);
required_string("$Anim:");
stuff_string(entry.anim_filename, F_NAME, NAME_LENGTH);
required_string("$AlwaysInTechRoom:");
stuff_int(&temp);
if (temp) {
// set default to align with what we read - Goober5000
entry.flags |= IIF_IN_TECH_DATABASE;
entry.flags |= IIF_DEFAULT_IN_TECH_DATABASE;
}
required_string("$Description:");
stuff_string(entry.desc, F_MULTITEXT);
if (intel_info_lookup(entry.name) >= 0)
error_display(0, "Duplicate entry %s in species.tbl!", entry.name);
else
Intel_info.push_back(entry);
}
Intel_inited = true;
}
catch (const parse::ParseException& e)
{
mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", "species.tbl", e.what()));
return;
}
}
void techroom_intel_reset()
{
Intel_info.clear();
Intel_inited = false;
}
void techroom_init()
{
int i;
techroom_buttons *b;
Ships_loaded = false;
Weapons_loaded = false;
Intel_loaded = false;
Techroom_show_all = 0;
// set up UI stuff
Ui_window.create(0, 0, gr_screen.max_w_unscaled, gr_screen.max_h_unscaled, 0);
Tech_background_bitmap = bm_load(Tech_background_filename[gr_screen.res]);
if (Tech_background_bitmap < 0) {
// failed to load bitmap, not a good thing
Warning(LOCATION,"Error loading techroom background bitmap %s", Tech_background_filename[gr_screen.res]);
}
Tech_background_bitmap_mask = bm_load(Tech_mask_filename[gr_screen.res]);
if (Tech_background_bitmap_mask < 0) {
Warning(LOCATION, "Error loading techroom background mask %s", Tech_mask_filename[gr_screen.res]);
return;
} else {
Ui_window.set_mask_bmap(Tech_mask_filename[gr_screen.res]);
}
for (i=0; i<NUM_BUTTONS; i++) {
b = &Buttons[gr_screen.res][i];
b->button.create(&Ui_window, "", b->x, b->y, 60, 30, b->flags & REPEAT, 1);
// set up callback for when a mouse first goes over a button
if (b->filename) {
b->button.set_bmaps(b->filename);
b->button.set_highlight_action(common_play_highlight_sound);
} else {
b->button.hide();
}
b->button.link_hotspot(b->hotspot);
}
// common tab button text
Ui_window.add_XSTR("Technical Database", 1055, Buttons[gr_screen.res][TECH_DATABASE_TAB].xt, Buttons[gr_screen.res][TECH_DATABASE_TAB].yt, &Buttons[gr_screen.res][TECH_DATABASE_TAB].button, UI_XSTR_COLOR_GREEN);
Ui_window.add_XSTR("Mission Simulator", 1056, Buttons[gr_screen.res][SIMULATOR_TAB].xt, Buttons[gr_screen.res][SIMULATOR_TAB].yt, &Buttons[gr_screen.res][SIMULATOR_TAB].button, UI_XSTR_COLOR_GREEN);
Ui_window.add_XSTR("Cutscenes", 1057, Buttons[gr_screen.res][CUTSCENES_TAB].xt, Buttons[gr_screen.res][CUTSCENES_TAB].yt, &Buttons[gr_screen.res][CUTSCENES_TAB].button, UI_XSTR_COLOR_GREEN);
Ui_window.add_XSTR("Credits", 1058, Buttons[gr_screen.res][CREDITS_TAB].xt, Buttons[gr_screen.res][CREDITS_TAB].yt, &Buttons[gr_screen.res][CREDITS_TAB].button, UI_XSTR_COLOR_GREEN);
// common ship/weapon/intel text
Ui_window.add_XSTR("Ships", 293, Buttons[gr_screen.res][SHIPS_DATA_TAB].xt, Buttons[gr_screen.res][SHIPS_DATA_TAB].yt, &Buttons[gr_screen.res][SHIPS_DATA_TAB].button, UI_XSTR_COLOR_GREEN);
Ui_window.add_XSTR("Weapons", 1553, Buttons[gr_screen.res][WEAPONS_DATA_TAB].xt, Buttons[gr_screen.res][WEAPONS_DATA_TAB].yt, &Buttons[gr_screen.res][WEAPONS_DATA_TAB].button, UI_XSTR_COLOR_GREEN);
Ui_window.add_XSTR("Intelligence", 1066, Buttons[gr_screen.res][INTEL_DATA_TAB].xt, Buttons[gr_screen.res][INTEL_DATA_TAB].yt, &Buttons[gr_screen.res][INTEL_DATA_TAB].button, UI_XSTR_COLOR_GREEN);
// common help/options/commit text
Ui_window.add_XSTR("Exit", 1418, Buttons[gr_screen.res][EXIT_BUTTON].xt, Buttons[gr_screen.res][EXIT_BUTTON].yt, &Buttons[gr_screen.res][EXIT_BUTTON].button, UI_XSTR_COLOR_PINK);
if (Player->flags & PLAYER_FLAGS_IS_MULTI) {
Buttons[gr_screen.res][SIMULATOR_TAB].button.disable();
Buttons[gr_screen.res][CUTSCENES_TAB].button.disable();
}
// set some hotkeys
Buttons[gr_screen.res][PREV_ENTRY_BUTTON].button.set_hotkey(KEY_LEFT);
Buttons[gr_screen.res][NEXT_ENTRY_BUTTON].button.set_hotkey(KEY_RIGHT);
Buttons[gr_screen.res][SCROLL_INFO_UP].button.set_hotkey(KEY_UP);
Buttons[gr_screen.res][SCROLL_INFO_DOWN].button.set_hotkey(KEY_DOWN);
for (i=0; i<LIST_BUTTONS_MAX; i++) {
List_buttons[i].create(&Ui_window, "", 0, 0, 60, 30, 0, 1);
List_buttons[i].hide();
List_buttons[i].disable();
}
View_window.create(&Ui_window, "", Tech_ship_display_coords[gr_screen.res][SHIP_X_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_Y_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_W_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_H_COORD], 1, 1);
View_window.hide();
Buttons[gr_screen.res][HELP_BUTTON].button.set_hotkey(KEY_F1);
Buttons[gr_screen.res][EXIT_BUTTON].button.set_hotkey(KEY_CTRLED | KEY_ENTER);
Buttons[gr_screen.res][SCROLL_LIST_UP].button.set_hotkey(KEY_PAGEUP);
Buttons[gr_screen.res][SCROLL_LIST_DOWN].button.set_hotkey(KEY_PAGEDOWN);
// init help overlay states
Techroom_overlay_id = help_overlay_get_index(TECH_ROOM_OVERLAY);
help_overlay_set_state(Techroom_overlay_id, gr_screen.res, 0);
// setup slider
Tech_slider.create(&Ui_window, Tech_slider_coords[gr_screen.res][SHIP_X_COORD], Tech_slider_coords[gr_screen.res][SHIP_Y_COORD], Tech_slider_coords[gr_screen.res][SHIP_W_COORD], Tech_slider_coords[gr_screen.res][SHIP_H_COORD], (int)Ship_info.size(), Tech_slider_filename[gr_screen.res], &tech_scroll_list_up, &tech_scroll_list_down, &tech_ship_scroll_capture);
// zero intel anim/bitmap stuff
for(auto & intel_item : Intel_list){
intel_item.animation.num_frames = 0;
intel_item.bitmap = -1;
}
mprintf(("Techroom successfully initialized, now changing tab...\n"));
techroom_change_tab(Tab);
}
void techroom_lists_reset()
{
//unload the current animation, we load another one for the new current entry
if (Tab != SHIPS_DATA_TAB)
techroom_unload_animation();
model_free_all();
Techroom_ship_modelnum = -1;
Techroom_ship_model_instance = -1;
// This can be cleared immediately because there are no anims or bitmaps associated.
Ship_list.clear();
Ships_loaded = false;
// now that we're sure all the bitmaps are released, clear the vectors.
Weapon_list.clear();
Weapons_loaded = false;
Intel_list.clear();
Intel_loaded = false;
}
void techroom_close()
{
fsspeech_stop();
techroom_lists_reset();
Techroom_show_all = 0;
if (Tech_background_bitmap != -1) {
bm_release(Tech_background_bitmap);
}
Ui_window.destroy();
if (Tech_background_bitmap_mask != -1) {
bm_release(Tech_background_bitmap_mask);
}
common_free_interface_palette(); // restore game palette
}
void techroom_do_frame(float frametime)
{
int i, k;
// If we don't have a mask, we don't have enough data to do anything with this screen.
if (Tech_background_bitmap_mask == -1) {
popup_game_feature_not_in_demo();
gameseq_post_event(GS_EVENT_MAIN_MENU);
return;
}
// turn off controls when overlay is on
if ( help_overlay_active(Techroom_overlay_id) ) {
Buttons[gr_screen.res][HELP_BUTTON].button.reset_status();
Ui_window.set_ignore_gadgets(1);
}
// turn off controls in trackball mode
if (Trackball_active) {
Ui_window.set_ignore_gadgets(1);
} else {
Ui_window.set_ignore_gadgets(0);
}
k = Ui_window.process() & ~KEY_DEBUGGED;
if ( (k > 0) || B1_JUST_RELEASED ) {
if ( help_overlay_active(Techroom_overlay_id) ) {
help_overlay_set_state(Techroom_overlay_id, gr_screen.res, 0);
Ui_window.set_ignore_gadgets(0);
k = 0;
}
}
if ( !help_overlay_active(Techroom_overlay_id) ) {
Ui_window.set_ignore_gadgets(0);
}
switch (k) {
case KEY_SHIFTED | KEY_TAB: // activate previous tab
i = Tab - 1;
if (i < 0) {
i = NUM_TABS - 1;
}
techroom_change_tab(i);
break;
case KEY_TAB: // activate next tab
i = Tab + 1;
if (i >= NUM_TABS) {
i = 0;
}
techroom_change_tab(i);
break;
case KEY_CTRLED | KEY_DOWN:
if ( !(Player->flags & PLAYER_FLAGS_IS_MULTI) ) {
techroom_button_pressed(SIMULATOR_TAB);
break;
}
// fall through
case KEY_CTRLED | KEY_UP:
techroom_button_pressed(CREDITS_TAB);
break;
case KEY_CTRLED | KEY_ENTER:
case KEY_ESC:
gameseq_post_event(GS_EVENT_MAIN_MENU);
break;
case KEY_CTRLED | KEY_SHIFTED | KEY_S:
Techroom_show_all = 1;
techroom_lists_reset();
techroom_change_tab(Tab);
break;
}
// check ship model window for activity
if (View_window.pressed()) {
Trackball_active = 1;
Trackball_mode = 1;
}
if (B1_RELEASED) {
Trackball_active = 0;
}
// check all da buttons
for (i=0; i<NUM_BUTTONS; i++) {
if (Buttons[gr_screen.res][i].button.pressed()) {
if (techroom_button_pressed(i)) {
return;
}
}
}
// check for mouseovers/clicks on the selection list
Select_tease_line = -1;
for (i=0; i<LIST_BUTTONS_MAX; i++) {
if (List_buttons[i].is_mouse_on()) {
Select_tease_line = i + List_offset;
}
if (List_buttons[i].pressed()) {
Cur_entry = i + List_offset;
gamesnd_play_iface(InterfaceSounds::USER_SELECT);
techroom_select_new_entry();
}
}
// clear & draw bg bitmap
GR_MAYBE_CLEAR_RES(Tech_background_bitmap);
if (Tech_background_bitmap >= 0) {
gr_set_bitmap(Tech_background_bitmap);
gr_bitmap(0, 0, GR_RESIZE_MENU);
}
// render
switch (Tab) {
case SHIPS_DATA_TAB:
techroom_ships_render(frametime);
break;
case WEAPONS_DATA_TAB:
case INTEL_DATA_TAB:
techroom_anim_render(frametime);
break;
}
Ui_window.draw();
for (i=TECH_DATABASE_TAB; i<=CREDITS_TAB; i++) {
if (Buttons[gr_screen.res][i].button.button_down()) {
break;
}
}
if (i > CREDITS_TAB) {
Buttons[gr_screen.res][TECH_DATABASE_TAB].button.draw_forced(2);
}
for (i=0; i<NUM_TABS; i++){
if (Buttons[gr_screen.res][i].button.button_down()){
break;
}
}
if (i == NUM_TABS){
Buttons[gr_screen.res][Tab].button.draw_forced(2);
}
// blit help overlay if active
help_overlay_maybe_blit(Techroom_overlay_id, gr_screen.res);
gr_flip();
}
// note: the name has to be pre-translated before being passed into this function
int intel_info_lookup(const char *name)
{
// bogus
if (!name)
return -1;
for (int i = 0; i < intel_info_size(); i++)
if (!stricmp(name, Intel_info[i].name))
return i;
return -1;
}
// Goober5000
void tech_reset_to_default()
{
// ships
for (auto& si : Ship_info)
{
si.flags.set(Ship::Info_Flags::In_tech_database, si.flags[Ship::Info_Flags::Default_in_tech_database]);
si.flags.set(Ship::Info_Flags::In_tech_database_m, si.flags[Ship::Info_Flags::Default_in_tech_database_m]);
}
// weapons
for (auto& wi : Weapon_info)
{
wi.wi_flags.set(Weapon::Info_Flags::In_tech_database, wi.wi_flags[Weapon::Info_Flags::Default_in_tech_database]);
}
// intelligence
for (int i = 0; i < intel_info_size(); ++i)
{
if (Intel_info[i].flags & IIF_DEFAULT_IN_TECH_DATABASE)
Intel_info[i].flags |= IIF_IN_TECH_DATABASE;
else
Intel_info[i].flags &= ~IIF_IN_TECH_DATABASE;
}
}