Files

1202 lines
34 KiB
C++

/*
* Copyright (C) Volition, Inc. 1999. All rights reserved.
*
* All source code herein is the property of Volition, Inc. You may not sell
* or otherwise commercially exploit the source or things you created based on the
* source.
*
*/
#include "gamesnd/gamesnd.h"
#include "hud/hudescort.h"
#include "hud/hudparse.h"
#include "hud/hudshield.h"
#include "hud/hudtargetbox.h"
#include "io/timer.h"
#include "network/multi.h"
#include "object/object.h"
#include "object/objectshield.h"
#include "parse/parselo.h"
#include "playerman/player.h"
#include "render/3d.h" //For g3_start_frame
#include "ship/ship.h"
#include "weapon/emp.h"
#include "graphics/matrix.h"
#define NUM_SHIELD_LEVELS 8
#define SHIELD_TRANSFER_PERCENT 0.083f // 1/12 total shield strength
#define SHIELD_HIT_DURATION_SHORT 300 // time a shield quadrant flashes after being hit
#define SHIELD_FLASH_INTERVAL_FAST 200 // time between shield quadrant flashes
// CommanderDJ - now dynamic
// #define MAX_SHIELD_ICONS 80
SCP_vector<SCP_string> Hud_shield_filenames;
SCP_vector<hud_frames> Shield_gauges;
static int Hud_shield_inited = 0;
/*static int Player_shield_coords[GR_NUM_RESOLUTIONS][2] =
{
{ // GR_640
396, 379
},
{ // GR_1024
634, 670
}
};
static int Target_shield_coords[GR_NUM_RESOLUTIONS][2] =
{
{ // GR_640
142, 379
},
{ // GR_1024
292, 670
}
};*/
int Shield_mini_coords[GR_NUM_RESOLUTIONS][2] = {
{ // GR_640
305, 291
},
{ // GR_1024
497, 470
}
};
// draw on the mini shield icon what the ship integrity is
int Hud_mini_3digit[GR_NUM_RESOLUTIONS][3] = {
{ // GR_640
310, 298, 0
},
{ // GR_1024
502, 477, 0
}
};
int Hud_mini_2digit[GR_NUM_RESOLUTIONS][3] = {
{ // GR_640
313, 298, 2
},
{ // GR_1024
506, 477, 2
}
};
int Hud_mini_1digit[GR_NUM_RESOLUTIONS][3] = {
{ // GR_640
316, 298, 6
},
{ // GR_1024
511, 477, 6
}
};
int Hud_mini_base[GR_NUM_RESOLUTIONS][2] = {
{ // GR_640
310, 298
},
{ // GR_1024
502, 477
}
};
int Shield_mini_loaded = 0;
hud_frames Shield_mini_gauge;
static shield_hit_info Shield_hit_data[num_shield_gauge_types];
// translate between clockwise-from-top shield quadrant ordering to way quadrants are numbered in the game
ubyte Quadrant_xlate[DEFAULT_SHIELD_SECTIONS] = {1,0,2,3};
// called at the start of each level from HUD_init. Use Hud_shield_init so we only init Shield_gauges[] once.
void hud_shield_level_init()
{
unsigned int i;
hud_frames temp;
hud_shield_hit_reset(Player_obj, 1); // reset for the player
if ( !Hud_shield_inited ) {
for ( i = 0; i < Hud_shield_filenames.size(); i++ ) {
Shield_gauges.push_back(temp);
Shield_gauges.at(i).first_frame = -1;
Shield_gauges.at(i).num_frames = 0;
}
Hud_shield_inited = 1;
}
// the shield mini gauge is loaded in HudGaugeShieldMini::pageIn()
// though it wouldn't hurt to keep this old failsafe code here
// since we are setting the status of if the mini shield was loaded
// --wookieejedi
if ( Shield_mini_gauge.first_frame == -1 ) {
Warning(LOCATION, "Could not load in the HUD shield ani \n");
return;
}
Shield_mini_loaded = 1;
}
int hud_shield_maybe_flash(int gauge, int target_index, int shield_offset)
{
int flashed = 0;
shield_hit_info *shi;
shi = &Shield_hit_data[target_index];
if ( shi->shield_hit_timers.empty() ) {
return 0;
}
if ( !timestamp_elapsed(shi->shield_hit_timers[shield_offset]) ) {
if ( timestamp_elapsed(shi->shield_hit_next_flash[shield_offset]) ) {
shi->shield_hit_next_flash[shield_offset] = timestamp(SHIELD_FLASH_INTERVAL_FAST);
shi->shield_show_bright ^= (1<<shield_offset); // toggle between default and bright frames
}
if ( shi->shield_show_bright & (1<<shield_offset) ) {
// hud_set_bright_color();
hud_set_gauge_color(gauge, HUD_C_BRIGHT);
flashed = 1;
} else {
hud_set_gauge_color(gauge, HUD_C_NORMAL);
// hud_set_default_color();
}
}
return flashed;
}
bool shield_ani_warning_displayed_already = false;
// called at beginning of level to page in all ship icons
// used in this level
void hud_ship_icon_page_in(const ship_info *sip)
{
hud_frames *sgp;
if ( sip->shield_icon_index == 255 ) {
return;
}
// load in shield frames if not already loaded
Assert(sip->shield_icon_index < (ubyte)Hud_shield_filenames.size());
sgp = &Shield_gauges.at(sip->shield_icon_index);
if ( sgp->first_frame == -1 ) {
sgp->first_frame = bm_load_animation(Hud_shield_filenames.at(sip->shield_icon_index).c_str(), &sgp->num_frames);
if ( sgp->first_frame == -1 ) {
Warning(LOCATION, "Could not load in the HUD shield ani: %s\n", Hud_shield_filenames.at(sip->shield_icon_index).c_str());
return;
}
}
int i;
for (i=0; i<sgp->num_frames; i++ ) {
bm_page_in_aabitmap(sgp->first_frame+i, 1);
}
}
// ------------------------------------------------------------------
// hud_shield_equalize()
//
// Equalize all shield quadrants for an object
//
void hud_shield_equalize(object *objp, player *pl)
{
float penalty;
Assert(objp != NULL);
if (objp == NULL)
return;
Assert(pl != NULL);
if (pl == NULL)
return;
Assert(objp->type == OBJ_SHIP);
if (objp->type != OBJ_SHIP)
return;
// Goober5000 - quick out if we have no shields
if (objp->flags[Object::Object_Flags::No_shields])
return;
// maybe impose a 2% penalty - server side and single player only
if (!MULTIPLAYER_CLIENT && ((pl->shield_penalty_stamp < 0) || timestamp_elapsed_safe(pl->shield_penalty_stamp, 1000)) ) {
penalty = 0.02f;
// reset the penalty timestamp
pl->shield_penalty_stamp = timestamp(1000);
} else {
penalty = 0.0f;
}
shield_balance(objp, 1, penalty);
// beep
if (objp == Player_obj) {
snd_play(gamesnd_get_game_sound(GameSounds::SHIELD_XFER_OK));
}
}
// ------------------------------------------------------------------
// hud_augment_shield_quadrant()
//
// Transfer shield energy to a shield quadrant from the three other
// quadrants. Works by trying to transfer a fixed amount of shield
// energy from the other three quadrants, taking the same percentage
// from each quadrant.
//
// input: objp => object to perform shield transfer on
// direction => which quadrant to augment:
// 0 - right
// 1 - top
// 2 - bottom
// 3 - left
//
void hud_augment_shield_quadrant(object *objp, int direction)
{
Assertion((direction >= 0) && (direction < 4), "Invalid quadrant index %i!", direction);
ship *shipp = &Ships[objp->instance];
ship_info *sip = &Ship_info[shipp->ship_info_index];
if (sip->flags[Ship::Info_Flags::Model_point_shields]) {
// Using model point shields, so map to the correct quadrant
direction = sip->shield_point_augment_ctrls[direction];
if (direction < 0) {
// This quadrant cannot be augmented, ignore request and bail
return;
}
} // Else, using standard shields.
shield_transfer(objp, direction, SHIELD_TRANSFER_PERCENT);
}
// Try to find a match between filename and the names inside
// of Hud_shield_filenames. This will provide us with an
// association of ship class to shield icon information.
void hud_shield_assign_info(ship_info *sip, const char *filename)
{
ubyte i;
for ( i = 0; i < (ubyte)Hud_shield_filenames.size(); i++ ) {
if ( !stricmp(filename, Hud_shield_filenames.at(i).c_str()) ) {
sip->shield_icon_index = i;
return;
}
}
//No HUD icon found. Add one!
sip->shield_icon_index = (unsigned char) Hud_shield_filenames.size();
Hud_shield_filenames.emplace_back(filename);
}
void hud_show_mini_ship_integrity(const object *objp, int x_force, int y_force)
{
char text_integrity[64];
int numeric_integrity;
float p_target_integrity;
int final_pos[2];
p_target_integrity = get_hull_pct(objp);
numeric_integrity = (int)std::lround(p_target_integrity * 100);
if(numeric_integrity > 100){
numeric_integrity = 100;
}
// Assert(numeric_integrity <= 100);
// 3 digit hull strength
if ( numeric_integrity == 100 ) {
final_pos[0] = x_force + Hud_mini_3digit[gr_screen.res][0] - Shield_mini_coords[gr_screen.res][0];
final_pos[1] = y_force + Hud_mini_3digit[gr_screen.res][1] - Shield_mini_coords[gr_screen.res][1];
}
// 1 digit hull strength
else if ( numeric_integrity < 10 ) {
final_pos[0] = x_force + Hud_mini_1digit[gr_screen.res][0] - Shield_mini_coords[gr_screen.res][0];
final_pos[1] = y_force + Hud_mini_1digit[gr_screen.res][1] - Shield_mini_coords[gr_screen.res][1];
}
// 2 digit hull strength
else {
final_pos[0] = x_force + Hud_mini_2digit[gr_screen.res][0] - Shield_mini_coords[gr_screen.res][0];
final_pos[1] = y_force + Hud_mini_2digit[gr_screen.res][1] - Shield_mini_coords[gr_screen.res][1];
}
if ( numeric_integrity == 0 ) {
if ( p_target_integrity > 0 ) {
numeric_integrity = 1;
}
}
sprintf(text_integrity, "%d", numeric_integrity);
if ( numeric_integrity < 100 ) {
hud_num_make_mono(text_integrity);
}
gr_string(final_pos[0], final_pos[1], text_integrity);
}
// Draw the miniature shield icon that is drawn near the reticle
// this function is only used by multi_ingame_join_display_ship() in multi_ingame.cpp as of the new HudGauge implementation (Swifty)
void hud_shield_show_mini(const object *objp, int x_force, int y_force, int x_hull_offset, int y_hull_offset)
{
float max_shield;
int hud_color_index, range, frame_offset;
int sx, sy, i;
if ( objp->type != OBJ_SHIP ) {
return;
}
hud_set_gauge_color(HUD_TARGET_MINI_ICON);
if (!Shield_mini_loaded)
return;
sx = (x_force == -1) ? Shield_mini_coords[gr_screen.res][0] : x_force;
sy = (y_force == -1) ? Shield_mini_coords[gr_screen.res][1] : y_force;
// draw the ship first
hud_shield_maybe_flash(HUD_TARGET_MINI_ICON, SHIELD_GAUGE_TARGET, Shield_hit_data[SHIELD_GAUGE_TARGET].hull_hit_index);
hud_show_mini_ship_integrity(objp, x_force + x_hull_offset,y_force + y_hull_offset);
// draw the four quadrants
// Draw shield quadrants at one of NUM_SHIELD_LEVELS
max_shield = shield_get_max_quad(objp);
int n_quadrants = static_cast<int>(objp->shield_quadrant.size());
for ( i = 0; i < n_quadrants; i++ ) {
if ( objp->flags[Object::Object_Flags::No_shields] || i >= DEFAULT_SHIELD_SECTIONS) {
break;
}
int num;
if (!(Ship_info[Ships[objp->instance].ship_info_index].flags[Ship::Info_Flags::Model_point_shields]))
num = Quadrant_xlate[i];
else
num = i;
if ( (max_shield > 0.0f) && (objp->shield_quadrant[num]/max_shield < Shield_percent_skips_damage) ) {
continue;
}
if ( hud_shield_maybe_flash(HUD_TARGET_MINI_ICON, SHIELD_GAUGE_TARGET, i) ) {
frame_offset = i+n_quadrants;
} else {
frame_offset = i;
}
range = HUD_color_alpha;
hud_color_index = static_cast<int>(std::lround((objp->shield_quadrant[num] / max_shield) * range));
Assert(hud_color_index >= 0 && hud_color_index <= range);
if ( hud_color_index < 0 ) {
hud_color_index = 0;
}
if ( hud_color_index >= HUD_NUM_COLOR_LEVELS ) {
hud_color_index = HUD_NUM_COLOR_LEVELS - 1;
}
if ( hud_gauge_maybe_flash(HUD_TARGET_MINI_ICON) == 1) {
// hud_set_bright_color();
hud_set_gauge_color(HUD_TARGET_MINI_ICON, HUD_C_BRIGHT);
} else {
// gr_set_color_fast(&HUD_color_defaults[hud_color_index]);
hud_set_gauge_color(HUD_TARGET_MINI_ICON, hud_color_index);
}
GR_AABITMAP(Shield_mini_gauge.first_frame + frame_offset, sx + HUD_nose_x, sy + HUD_nose_y);
}
// hud_set_default_color();
}
// reset the shield_hit_info data structure
// pass NULL as objp if you only need to initialize a shield_hit_info without an
// associated ship
void shield_info_reset(const object *objp, shield_hit_info *shi)
{
int n_quadrants = (objp != nullptr) ? static_cast<int>(objp->shield_quadrant.size()) : 0;
shi->members = n_quadrants + 1;
shi->hull_hit_index = n_quadrants;
shi->shield_hit_timers.resize(shi->members);
shi->shield_hit_next_flash.resize(shi->members);
for ( int i = 0; i < shi->members; i++ ) {
shi->shield_hit_timers[i] = timestamp(0);
shi->shield_hit_next_flash[i] = timestamp(0);
}
shi->shield_hit_status = 0;
shi->shield_show_bright = 0;
}
// reset the timers and hit flags for the shield gauges
//
// This needs to be called whenever the player selects a new target
//
// input: player => optional parameter (default value 0). This is to indicate that player shield hit
// info should be reset. This is normally not the case.
// is for the player's current target
void hud_shield_hit_reset(const object *objp, int player)
{
shield_hit_info *shi;
if (player) {
shi = &Shield_hit_data[SHIELD_GAUGE_PLAYER];
} else {
shi = &Shield_hit_data[SHIELD_GAUGE_TARGET];
}
shield_info_reset(objp, shi);
}
// called once per frame to update the state of Shield_hit_status based on the Shield_hit_timers[]
void hud_shield_hit_update()
{
int i, j, limit;
limit = 1;
if ( Player_ai->target_objnum >= 0 ) {
limit = 2;
}
for ( i = 0; i < limit; i++ ) {
for ( j = 0; j < Shield_hit_data[i].members; j++ ) {
if ( timestamp_elapsed(Shield_hit_data[i].shield_hit_timers[j]) ) {
Shield_hit_data[i].shield_hit_status &= ~(1<<j);
Shield_hit_data[i].shield_show_bright &= ~(1<<j);
} else {
if ( timestamp_elapsed(Shield_hit_data[i].shield_hit_next_flash[j]) ) {
Shield_hit_data[i].shield_hit_next_flash[j] = timestamp(SHIELD_FLASH_INTERVAL_FAST);
Shield_hit_data[i].shield_show_bright ^= (1<<j); // toggle between default and bright frames
}
}
}
}
}
// called when a shield quadrant is struct, so we can update the timer that will draw the quadrant
// as flashing
//
// input:
// objp => object pointer for ship that has been hit
// quadrant => quadrant of shield getting hit (-1 if no shield is present)
void hud_shield_quadrant_hit(const object *objp, int quadrant)
{
shield_hit_info *shi;
int num;
if (Game_mode & GM_STANDALONE_SERVER)
return;
Assertion(objp != NULL, "hud_shield_quadrant_hit() called with a NULL objp; get a coder!\n");
if ( objp->type != OBJ_SHIP )
return;
hud_escort_ship_hit(objp, quadrant);
hud_gauge_popup_start(HUD_TARGET_MINI_ICON);
if ( OBJ_INDEX(objp) == Player_ai->target_objnum ) {
shi = &Shield_hit_data[SHIELD_GAUGE_TARGET];
} else if ( objp == Player_obj ) {
shi = &Shield_hit_data[SHIELD_GAUGE_PLAYER];
} else {
return;
}
Assertion(!shi->shield_hit_timers.empty(), "Shield hit info object for object '%s' has a size " SIZE_T_ARG " shield_hit_timers; get a coder!\n", Ships[objp->instance].ship_name, shi->shield_hit_timers.size());
Assertion(shi->hull_hit_index < (int) shi->shield_hit_timers.size(), "Shield hit info object for object '%s' has a hull_hit_index of %d (should be between 0 and " SIZE_T_ARG "); get a coder!\n", Ships[objp->instance].ship_name, shi->hull_hit_index, shi->shield_hit_timers.size() - 1);
if ( quadrant >= 0 ) {
if ( !(Ship_info[Ships[objp->instance].ship_info_index].flags[Ship::Info_Flags::Model_point_shields]) )
num = Quadrant_xlate[quadrant];
else
num = quadrant;
Assertion(num < shi->hull_hit_index, "Shield hit info object for object '%s' hit on quadrant #%d, despite having a hull_hit_index of %d; get a coder!\n", Ships[objp->instance].ship_name, num, shi->hull_hit_index);
shi->shield_hit_timers[num] = timestamp(SHIELD_HIT_DURATION_SHORT);
} else {
shi->shield_hit_timers[shi->hull_hit_index] = timestamp(SHIELD_HIT_DURATION_SHORT);
hud_targetbox_start_flash(TBOX_FLASH_HULL);
}
}
HudGaugeShield::HudGaugeShield():
HudGauge(HUD_OBJECT_PLAYER_SHIELD, HUD_PLAYER_SHIELD_ICON, false, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255)
{
}
HudGaugeShield::HudGaugeShield(int _gauge_object, int _gauge_config):
HudGauge(_gauge_object, _gauge_config, false, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255)
{
}
void HudGaugeShield::render(float /*frametime*/, bool /*config*/)
{
}
void HudGaugeShield::showShields(const object *objp, ShieldGaugeType mode, bool config)
{
if (!config && objp->type != OBJ_SHIP )
return;
// Goober5000 - don't show if primitive sensors
if (!config && Ships[Player_obj->instance].flags[Ship::Ship_Flags::Primitive_sensors] )
return;
ship* sp = nullptr;
ship_info* sip = nullptr;
if (!config) {
sp = &Ships[objp->instance];
sip = &Ship_info[sp->ship_info_index];
} else {
SCP_string ship_name;
// Now check if the current HUD specifies a different ship
if (SCP_vector_inbounds(HC_available_huds, HC_chosen_hud)) {
SCP_string hud = HC_available_huds[HC_chosen_hud].second;
if (auto it = HC_hud_shield_ships.find(hud); it != HC_hud_shield_ships.end()) {
ship_name = it->second[mode];
}
}
// If we don't have a specific setting then let's try using the global setting
if (ship_name.empty()) {
if (auto it = HC_hud_shield_ships.find("default"); it != HC_hud_shield_ships.end()) {
ship_name = it->second[mode];
}
}
// If we still don't have something then try to use the FS2 retail default
if (ship_name.empty()) {
ship_name = "gtf myrmidon";
}
// Try to find the ship with the name specified in the config
for (ship_info& ship : Ship_info) {
if (!stricmp(ship.name, ship_name.c_str()) &&
(ship.shield_icon_index != 255 || (ship.flags[Ship::Info_Flags::Generate_hud_icon]))) {
sip = &ship;
break;
}
}
// Couldn't find it so just get the first ship with shields
if (sip == nullptr) {
for (ship_info& ship : Ship_info) {
if (ship.shield_icon_index != 255 || (ship.flags[Ship::Info_Flags::Generate_hud_icon])) {
sip = &ship;
break;
}
}
}
}
// If we still don't have a ship to display, bail
if (sip == nullptr) {
return;
}
if (sip->shield_icon_index == 255 && !(sip->flags[Ship::Info_Flags::Generate_hud_icon]) /*&& !digitus_improbus*/) {
return;
}
setGaugeColor(HUD_C_NONE, config);
std::shared_ptr<hud_frames> sgp = nullptr;
// load in shield frames if not already loaded
if (sip->shield_icon_index != 255) {
if (!config) {
sgp = std::shared_ptr<hud_frames>(&Shield_gauges.at(sip->shield_icon_index), [](hud_frames*) {
/* Do nothing, managed externally */
});
} else {
sgp = std::make_unique<hud_frames>();
sgp->first_frame = -1;
sgp->num_frames = 0;
}
if (config || (sgp != nullptr && (sgp->first_frame < 0) && (sip->shield_icon_index < Hud_shield_filenames.size())) ) {
sgp->first_frame = bm_load_animation(Hud_shield_filenames.at(sip->shield_icon_index).c_str(), &sgp->num_frames);
if (sgp->first_frame == -1) {
if (!shield_ani_warning_displayed_already) {
shield_ani_warning_displayed_already = true;
Warning(LOCATION, "Could not load in the HUD shield ani: %s\n", Hud_shield_filenames.at(sip->shield_icon_index).c_str());
}
return;
}
}
}
int x = position[0];
int y = position[1];
float scale = 1.0;
if (config) {
std::tie(x, y, scale) = hud_config_convert_coord_sys(position[0], position[1], base_w, base_h);
}
// Make copies so we can modify them
int sx = x;
int sy = y;
// draw the ship first
if (!config) {
maybeFlashShield(SHIELD_GAUGE_PLAYER, Shield_hit_data[SHIELD_GAUGE_PLAYER].hull_hit_index);
}
if(sip->shield_icon_index != 255)
{
renderBitmap(sgp->first_frame, sx, sy, scale, config);
}
else
{
GR_DEBUG_SCOPE("Render generated shield icon");
bool g3_yourself = !g3_in_frame();
angles rot_angles = {-1.570796327f,0.0f,0.0f};
matrix object_orient;
vm_angles_2_matrix(&object_orient, &rot_angles);
const int CLIP_WIDTH = fl2i(112 * scale);
const int CLIP_HEIGHT = fl2i(93 * scale);
gr_screen.clip_width = CLIP_WIDTH;
gr_screen.clip_height = CLIP_HEIGHT;
//Fire it up
if(g3_yourself)
g3_start_frame(1);
hud_save_restore_camera_data(1);
setClip(sx, sy, CLIP_WIDTH, CLIP_HEIGHT);
g3_set_view_matrix( &sip->closeup_pos, &vmd_identity_matrix, sip->closeup_zoom * 2.5f);
gr_set_proj_matrix(Proj_fov * 0.5f, gr_screen.clip_aspect, Min_draw_distance, Max_draw_distance);
gr_set_view_matrix(&Eye_position, &Eye_matrix);
//We're ready to show stuff
{
model_render_params render_info;
// If this comment is here then I have not tested this
int model_instance_number = -1;
if (!config) {
model_instance_number = sp->model_instance_num;
render_info.set_replacement_textures(model_get_instance(model_instance_number)->texture_replace);
}
if (sip->model_num < 0 && config) {
sip->model_num = model_load(sip, false);
}
render_info.set_flags(MR_NO_LIGHTING | MR_AUTOCENTER | MR_NO_FOGGING);
render_info.set_detail_level_lock(1);
render_info.set_object_number(OBJ_INDEX(objp));
model_render_immediate( &render_info, sip->model_num, model_instance_number, &object_orient, &vmd_zero_vector );
}
//We're done
gr_end_view_matrix();
gr_end_proj_matrix();
if(g3_yourself)
g3_end_frame();
hud_save_restore_camera_data(0);
resetClip();
}
if(!sip->max_shield_strength)
return;
// Draw shield quadrants at one of NUM_SHIELD_LEVELS
float max_shield = config ? 100.0f : shield_get_max_quad(objp);
coord2d shield_icon_coords[6];
int n_quadrants = config ? DEFAULT_SHIELD_SECTIONS : static_cast<int>(objp->shield_quadrant.size());
for (int i = 0; i < n_quadrants; i++) {
if (!config && objp->flags[Object::Object_Flags::No_shields]) {
break;
}
if ( (!config) && (max_shield > 0.0f) ) {
if (!(sip->flags[Ship::Info_Flags::Model_point_shields])) {
if (objp->shield_quadrant[Quadrant_xlate[i]]/max_shield < Shield_percent_skips_damage)
continue;
} else {
if (objp->shield_quadrant[i]/max_shield < Shield_percent_skips_damage)
continue;
}
}
GR_DEBUG_SCOPE("Render shield quadrant");
int range = MAX(HUD_COLOR_ALPHA_MAX, HUD_color_alpha + n_quadrants);
int hud_color_index = HUD_C_NONE;
if (!config) {
if (!(sip->flags[Ship::Info_Flags::Model_point_shields]))
hud_color_index = fl2i((objp->shield_quadrant[Quadrant_xlate[i]] / max_shield) * range);
else
hud_color_index = fl2i((objp->shield_quadrant[i] / max_shield) * range);
Assert(hud_color_index >= 0 && hud_color_index <= range);
if (hud_color_index < 0) {
hud_color_index = 0;
}
if (hud_color_index >= HUD_NUM_COLOR_LEVELS) {
hud_color_index = HUD_NUM_COLOR_LEVELS - 1;
}
}
int flash=0;
if (!config) {
flash = maybeFlashShield(mode, i);
}
if ( !flash ) {
// gr_set_color_fast(&HUD_color_defaults[hud_color_index]);
setGaugeColor(hud_color_index, config);
if(sip->shield_icon_index != 255)
{
int framenum = sgp->first_frame+i+1;
if (framenum < sgp->first_frame + sgp->num_frames) {
renderBitmap(framenum, sx, sy, scale, config);
}
if (config) {
int bmw, bmh;
bm_get_info(sgp->first_frame, &bmw, &bmh);
hud_config_set_mouse_coords(gauge_config_id, x, x + fl2i(bmw * scale), y, y + fl2i(bmh * scale));
}
}
else
{
//Ugh, draw four shield quadrants
static const int TRI_EDGE = fl2i(6 * scale);
static const int BAR_LENGTH = fl2i(112 * scale);
static const int BAR_HEIGHT = fl2i(63 * scale);
static const int BAR_WIDTH = fl2i(6 * scale);
static const int SHIELD_OFFSET = fl2i(BAR_WIDTH + TRI_EDGE + 3 * scale);
// If this comment is here then I have not tested this
if (config) {
hud_config_set_mouse_coords(gauge_config_id, x, x + BAR_LENGTH, y, y + BAR_HEIGHT);
}
switch(i)
{
//Top
case 0:
shield_icon_coords[0].x = sx; shield_icon_coords[0].y = sy+BAR_WIDTH+TRI_EDGE;
shield_icon_coords[1].x = sx; shield_icon_coords[1].y = sy;
shield_icon_coords[2].x = sx+TRI_EDGE; shield_icon_coords[2].y = sy+BAR_WIDTH;
shield_icon_coords[3].x = sx+BAR_LENGTH; shield_icon_coords[3].y = sy;
shield_icon_coords[4].x = sx+BAR_LENGTH-TRI_EDGE; shield_icon_coords[4].y = sy+BAR_WIDTH;
shield_icon_coords[5].x = sx+BAR_LENGTH; shield_icon_coords[5].y = sy+BAR_WIDTH+TRI_EDGE;
renderShieldIcon(shield_icon_coords);
break;
//Left
case 3:
sy += SHIELD_OFFSET;
shield_icon_coords[0].x = sx+BAR_WIDTH+TRI_EDGE; shield_icon_coords[0].y = sy+BAR_HEIGHT;
shield_icon_coords[1].x = sx; shield_icon_coords[1].y = sy+BAR_HEIGHT;
shield_icon_coords[2].x = sx+BAR_WIDTH; shield_icon_coords[2].y = sy+BAR_HEIGHT-TRI_EDGE;
shield_icon_coords[3].x = sx; shield_icon_coords[3].y = sy;
shield_icon_coords[4].x = sx+BAR_WIDTH; shield_icon_coords[4].y = sy+TRI_EDGE;
shield_icon_coords[5].x = sx+BAR_WIDTH+TRI_EDGE; shield_icon_coords[5].y = sy;
renderShieldIcon(shield_icon_coords);
sy -= SHIELD_OFFSET + BAR_WIDTH + TRI_EDGE;
break;
//Right
case 1:
sx += BAR_LENGTH;
sy += SHIELD_OFFSET;
shield_icon_coords[0].x = sx-BAR_WIDTH-TRI_EDGE; shield_icon_coords[0].y = sy;
shield_icon_coords[1].x = sx; shield_icon_coords[1].y = sy;
shield_icon_coords[2].x = sx-BAR_WIDTH; shield_icon_coords[2].y = sy+TRI_EDGE;
shield_icon_coords[3].x = sx; shield_icon_coords[3].y = sy+BAR_HEIGHT;
shield_icon_coords[4].x = sx-BAR_WIDTH; shield_icon_coords[4].y = sy+BAR_HEIGHT-TRI_EDGE;
shield_icon_coords[5].x = sx-BAR_WIDTH-TRI_EDGE; shield_icon_coords[5].y = sy+BAR_HEIGHT;
renderShieldIcon(shield_icon_coords);
sx -= BAR_LENGTH;
sy -= SHIELD_OFFSET;
break;
//Bottom
case 2:
sy += BAR_HEIGHT + SHIELD_OFFSET*2 - BAR_WIDTH - TRI_EDGE;
shield_icon_coords[0].x = sx+BAR_LENGTH; shield_icon_coords[0].y = sy;
shield_icon_coords[1].x = sx+BAR_LENGTH; shield_icon_coords[1].y = sy+BAR_WIDTH+TRI_EDGE;
shield_icon_coords[2].x = sx+BAR_LENGTH-TRI_EDGE; shield_icon_coords[2].y = sy+TRI_EDGE;
shield_icon_coords[3].x = sx; shield_icon_coords[3].y = sy+BAR_WIDTH+TRI_EDGE;
shield_icon_coords[4].x = sx+TRI_EDGE; shield_icon_coords[4].y = sy+TRI_EDGE;
shield_icon_coords[5].x = sx; shield_icon_coords[5].y = sy;
renderShieldIcon(shield_icon_coords);
sy -= BAR_HEIGHT + SHIELD_OFFSET*2 - BAR_WIDTH - TRI_EDGE;
break;
//Whoops?
default:
nprintf(("HUD", "Invalid shield quadrant %d specified!\n", i));
break;
}
}
}
}
// hud_set_default_color();
}
/*
* Render a shield icon - basic shape is:
* 1 3
* ***************************
* ***************************
* ** 2 4 **
* * *
* 0 5
*
* Defined by 6 points, must be passed in the order show above (i.e. a valid triangle strip)
*
*/
void HudGaugeShield::renderShieldIcon(coord2d coords[6])
{
int nx = 0, ny = 0, i;
if ( gr_screen.rendering_to_texture != -1 ) {
gr_set_screen_scale(canvas_w, canvas_h, -1, -1, target_w, target_h, target_w, target_h, true);
// Respect the rendering display offset specified in the table
nx = display_offset_x;
ny = display_offset_y;
// Transfer the offset position into actual texture coordinates
gr_unsize_screen_pos(&nx, &ny);
} else {
if ( reticle_follow ) {
nx = HUD_nose_x;
ny = HUD_nose_y;
gr_resize_screen_pos(&nx, &ny);
gr_set_screen_scale(base_w, base_h);
gr_unsize_screen_pos(&nx, &ny);
} else {
gr_set_screen_scale(base_w, base_h);
}
}
for (i = 0; i < 6; ++i) {
coords[i].x += nx;
coords[i].y += ny;
}
//gr_shield_icon(coords);
g3_render_shield_icon(coords);
gr_reset_screen_scale();
}
int HudGaugeShield::maybeFlashShield(ShieldGaugeType target_index, int shield_offset)
{
int flashed = 0;
shield_hit_info *shi;
shi = &Shield_hit_data[target_index];
if ( !timestamp_elapsed(shi->shield_hit_timers[shield_offset]) ) {
if ( shi->shield_show_bright & (1<<shield_offset) ) {
// hud_set_bright_color();
setGaugeColor(HUD_C_BRIGHT);
flashed = 1;
} else {
setGaugeColor(HUD_C_NORMAL);
// hud_set_default_color();
}
}
return flashed;
}
HudGaugeShieldPlayer::HudGaugeShieldPlayer():
HudGaugeShield(HUD_OBJECT_PLAYER_SHIELD, HUD_PLAYER_SHIELD_ICON)
{
}
void HudGaugeShieldPlayer::render(float /*frametime*/, bool config)
{
object* player = nullptr;
if (!config) {
player = Player_obj;
}
showShields(player, SHIELD_GAUGE_PLAYER, config);
}
HudGaugeShieldTarget::HudGaugeShieldTarget():
HudGaugeShield(HUD_OBJECT_TARGET_SHIELD, HUD_TARGET_SHIELD_ICON)
{
}
void HudGaugeShieldTarget::render(float /*frametime*/, bool config)
{
object* targetp = nullptr;
if (!config) {
if (Player_ai->target_objnum == -1)
return;
targetp = &Objects[Player_ai->target_objnum];
// check to see if there is even a current target
if (targetp == &obj_used_list) {
return;
}
if (targetp == Player_obj)
return;
}
showShields(targetp, SHIELD_GAUGE_TARGET, config);
}
HudGaugeShieldMini::HudGaugeShieldMini(): // HUD_TARGET_MINI_ICON
HudGauge(HUD_OBJECT_MINI_SHIELD, HUD_TARGET_MINI_ICON, true, false, (VM_EXTERNAL | VM_DEAD_VIEW | VM_WARP_CHASE | VM_PADLOCK_ANY | VM_OTHER_SHIP), 255, 255, 255)
{
}
void HudGaugeShieldMini::init3DigitOffsets(int x, int y)
{
Mini_3digit_offsets[0] = x;
Mini_3digit_offsets[1] = y;
}
void HudGaugeShieldMini::init1DigitOffsets(int x, int y)
{
Mini_1digit_offsets[0] = x;
Mini_1digit_offsets[1] = y;
}
void HudGaugeShieldMini::init2DigitOffsets(int x, int y)
{
Mini_2digit_offsets[0] = x;
Mini_2digit_offsets[1] = y;
}
void HudGaugeShieldMini::initBitmaps(const char *fname)
{
Shield_mini_gauge.first_frame = bm_load_animation(fname, &Shield_mini_gauge.num_frames);
if ( Shield_mini_gauge.first_frame == -1 ) {
Warning(LOCATION, "Could not load in the HUD shield ani: %s\n", fname);
}
}
void HudGaugeShieldMini::render(float /*frametime*/, bool config)
{
object* targetp = nullptr;
if (!config) {
if (Player_ai->target_objnum == -1)
return;
targetp = &Objects[Player_ai->target_objnum];
// check to see if there is even a current target
if (targetp == &obj_used_list) {
return;
}
}
showMiniShields(targetp, config);
}
void HudGaugeShieldMini::pageIn()
{
bm_page_in_aabitmap( Shield_mini_gauge.first_frame, Shield_mini_gauge.num_frames );
}
// Draw the miniature shield icon that is drawn near the reticle
void HudGaugeShieldMini::showMiniShields(const object *objp, bool config)
{
if (Shield_mini_gauge.first_frame < 0)
return;
if (!config && objp->type != OBJ_SHIP ) {
return;
}
int x = position[0];
int y = position[1];
float scale = 1.0;
if (config) {
std::tie(x, y, scale) = hud_config_convert_coord_sys(position[0], position[1], base_w, base_h);
int bmw, bmh;
bm_get_info(Shield_mini_gauge.first_frame, &bmw, &bmh);
hud_config_set_mouse_coords(gauge_config_id, x, x + static_cast<int>(bmw * scale), y, y + static_cast<int>(bmh * scale));
}
setGaugeColor(HUD_C_NONE, config);
// draw the ship first
if (!config) {
maybeFlashShield(SHIELD_GAUGE_TARGET, Shield_hit_data[SHIELD_GAUGE_TARGET].hull_hit_index);
}
showIntegrity(config ? 1.0f : get_hull_pct(objp), config);
// draw the four quadrants
// Draw shield quadrants at one of NUM_SHIELD_LEVELS
float max_shield;
if (!config) {
max_shield = shield_get_max_quad(objp);
} else {
max_shield = 100.0f;
}
int n_quadrants = config ? DEFAULT_SHIELD_SECTIONS : static_cast<int>(objp->shield_quadrant.size());
for (int i = 0; i < n_quadrants; i++) {
if (!config && objp->flags[Object::Object_Flags::No_shields] ) {
break;
}
int num;
if (!config && !(Ship_info[Ships[objp->instance].ship_info_index].flags[Ship::Info_Flags::Model_point_shields]))
num = Quadrant_xlate[i];
else
num = i;
if ( (!config) && (max_shield > 0.0f) && (objp->shield_quadrant[num]/max_shield < Shield_percent_skips_damage) ) {
continue;
}
int frame_offset;
if (!config && maybeFlashShield(SHIELD_GAUGE_TARGET, i) ) {
frame_offset = i+n_quadrants;
} else {
frame_offset = i;
}
int hud_color_index = HUD_C_NONE;
if (!config) {
int range = HUD_color_alpha;
hud_color_index = (int)std::lround((objp->shield_quadrant[num] / max_shield) * range);
Assert(hud_color_index >= 0 && hud_color_index <= range);
if (hud_color_index < 0) {
hud_color_index = 0;
}
if (hud_color_index >= HUD_NUM_COLOR_LEVELS) {
hud_color_index = HUD_NUM_COLOR_LEVELS - 1;
}
}
if (!config && maybeFlashSexp() == 1) {
// hud_set_bright_color();
setGaugeColor(HUD_C_BRIGHT);
} else {
// gr_set_color_fast(&HUD_color_defaults[hud_color_index]);
setGaugeColor(hud_color_index, config);
}
if (frame_offset < Shield_mini_gauge.num_frames)
renderBitmap(Shield_mini_gauge.first_frame + frame_offset, x, y, scale, config);
}
// hud_set_default_color();
}
void HudGaugeShieldMini::showIntegrity(float p_target_integrity, bool config)
{
int numeric_integrity = (int)std::lround(p_target_integrity * 100);
if(numeric_integrity > 100){
numeric_integrity = 100;
}
// Assert(numeric_integrity <= 100);
// 3 digit hull strength
int final_pos[2];
if ( numeric_integrity == 100 ) {
memcpy(final_pos, Mini_3digit_offsets, sizeof(final_pos));
}
// 1 digit hull strength
else if ( numeric_integrity < 10 ) {
memcpy(final_pos, Mini_1digit_offsets, sizeof(final_pos));
}
// 2 digit hull strength
else {
memcpy(final_pos, Mini_2digit_offsets, sizeof(final_pos));
}
if ( numeric_integrity == 0 ) {
if ( p_target_integrity > 0 ) {
numeric_integrity = 1;
}
}
int x = position[0];
int y = position[1];
float scale = 1.0;
if (config) {
std::tie(x, y, scale) = hud_config_convert_coord_sys(position[0], position[1], base_w, base_h);
}
final_pos[0] = static_cast<int>(final_pos[0] * scale);
final_pos[1] = static_cast<int>(final_pos[1] * scale);
final_pos[0] += x;
final_pos[1] += y;
char text_integrity[64];
sprintf(text_integrity, "%d", numeric_integrity);
if ( numeric_integrity < 100 ) {
hud_num_make_mono(text_integrity, font_num);
}
renderString(final_pos[0], final_pos[1], text_integrity, scale, config);
}
int HudGaugeShieldMini::maybeFlashShield(int target_index, int shield_offset)
{
int flashed = 0;
shield_hit_info *shi;
shi = &Shield_hit_data[target_index];
if ( !timestamp_elapsed(shi->shield_hit_timers[shield_offset]) ) {
if ( shi->shield_show_bright & (1<<shield_offset) ) {
// hud_set_bright_color();
setGaugeColor(HUD_C_BRIGHT);
flashed = 1;
} else {
setGaugeColor(HUD_C_NORMAL);
// hud_set_default_color();
}
}
return flashed;
}