/* client/hud.cc This file is part of the Osirion project and is distributed under the terms and conditions of the GNU General Public License version 2 */ #include "core/core.h" #include "core/range.h" #include "client/client.h" #include "client/hud.h" #include "client/hudenginestatus.h" #include "client/hudplayerstatus.h" #include "client/hudtargetstatus.h" #include "client/input.h" #include "client/targets.h" #include "render/render.h" #include "render/renderext.h" #include "ui/ui.h" #include "ui/paint.h" namespace client { const float SPLASH_POPUP_DELAY = 0.1f; HUD::HUD(ui::Widget *parent) : Widget(parent) { set_label("hud"); set_border(false); set_background(false); hud_center = new ui::Bitmap(this, "bitmaps/pointers/center"); hud_center->set_color(palette()->pointer()); // player status hud_playerstatus = new HUDPlayerStatus(this); // engine status hud_enginestatus = new HUDEngineStatus(this); // target status hud_targetstatus = new HUDTargetStatus(this); hud_splash_time = 0.0f; hud_last_target = 0; } void HUD::resize() { const float padding = ui::root()->font_large()->height(); hud_center->set_size(ui::UI::pointer_size, ui::UI::pointer_size); hud_center->set_location((size() - hud_center->size()) * 0.5f); const float w = (width() - 4 * padding) / 3.0f; hud_playerstatus->set_size(w,ui::UI::elementsize.height() * 3.0f); hud_playerstatus->set_location(padding, height() - hud_playerstatus->height() - padding); hud_enginestatus->set_size(w,ui::UI::elementsize.height() * 3.0f); hud_enginestatus->set_location(hud_playerstatus->right() + padding, hud_playerstatus->top()); hud_targetstatus->set_size(w,ui::UI::elementsize.height() * 3.0f); hud_targetstatus->set_location(hud_enginestatus->right() + padding, hud_playerstatus->top()); } /* * This function is called to draw off-screen target indicators * */ void HUD::draw_offscreen_target(core::Entity *entity, bool is_active_target) { const core::EntityControlable *controlable = ( (entity->type() == core::Entity::Controlable) ? static_cast(entity) : 0 ); math::Vector3f target(entity->location() - render::camera().location()); target = render::camera().axis().transpose() * target; math::Vector2f screen_edge; if (target.y() * target.y() + target.z() * target.z() < 0.0001) { // X - bound, behind (front is visible) screen_edge.assign(0.0f, -0.5f); } else if (fabs(target.y()) > fabs(target.z())) { // Y-bound screen_edge.assign(math::sgnf(target.y()) * 0.5f, 0.5f * target.z() / fabs(target.y())); } else { // Z-bound screen_edge.assign(0.5f * target.y() / fabs(target.z()), math::sgnf(target.z()) * 0.5f); } screen_edge[0] = (0.5f - screen_edge[0]) * ((float) render::State::width()); screen_edge[1] = (0.5f - screen_edge[1]) * ((float) render::State::height()); const float bitmap_margin = 24.0f; float bitmap_radius = 24.0f; if (is_active_target) { bitmap_radius = 32.0f; } else if (controlable) { if (render::ext_render(controlable)->distance() > core::range::fxdistance) { if (controlable->owner()) { bitmap_radius = 24.0f; } else { return; } } else { if (controlable->owner()) { bitmap_radius = 32.0f; } else { bitmap_radius = 16.0f; } } } math::Vector2f screen_center((float) render::State::width() * 0.5f, (float) render::State::height() * 0.5f); math::Vector2f bitmap_up((screen_edge - screen_center)); bitmap_up /= bitmap_up.length(); math::Vector2f bitmap_left(-bitmap_up.y(), bitmap_up.x()); math::Vector2f bitmap_center(screen_edge - (bitmap_up * (bitmap_margin + bitmap_radius))); // popup effect if (is_active_target && (hud_splash_time > 0.0f)) { bitmap_radius *= (core::application()->time() - hud_splash_time) / SPLASH_POPUP_DELAY; } bitmap_up *= bitmap_radius; bitmap_left *= bitmap_radius; math::Color bitmap_color; const float reputation = core::localplayer()->reputation(entity->faction()); std:: string bitmap_material("bitmaps/hud/offscreen_default"); if (controlable) { if (controlable->owner()) { bitmap_material.assign("bitmaps/hud/offscreen_controlable"); } // reputation color if (reputation >= core::range::reputation_friendly) { bitmap_color.assign(0.0f, 1.0f, 0.0f); // green } else if (reputation <= core::range::reputation_hostile) { bitmap_color.assign(1.0f, 0.0f, 0.0f); // red } else { if (controlable->owner()) { bitmap_color.assign(1.0f); // white } else { bitmap_color.assign(0.75f); // gray } } } else if (entity->has_flag(core::Entity::Dockable)) { bitmap_material.assign("bitmaps/hud/offscreen_dockable"); // reputation color if (reputation >= core::range::reputation_friendly) { bitmap_color.assign(0.0f, 1.0f, 0.0f); // green } else if (reputation <= core::range::reputation_hostile) { bitmap_color.assign(1.0f, 0.0f, 0.0f); // red } else { bitmap_color.assign(0.75f); // gray } } else { bitmap_color.assign(palette()->text()); } if (entity == core::localplayer()->mission_target()) { bitmap_color.assign(palette()->mission()); } else if (entity == core::localplayer()->autopilot_target()) { bitmap_color.assign(palette()->mission()); } render::Textures::bind(bitmap_material.c_str()); gl::color(bitmap_color); gl::enable(GL_TEXTURE_2D); gl::begin(gl::Quads); gl::texcoord(0.0f, 0.0f); gl::vertex(bitmap_center + bitmap_up - bitmap_left); gl::texcoord(1.0f, 0.0f); gl::vertex(bitmap_center + bitmap_up + bitmap_left); gl::texcoord(1.0f, 1.0f); gl::vertex(bitmap_center - bitmap_up + bitmap_left); gl::texcoord(0.0f, 1.0f); gl::vertex(bitmap_center - bitmap_up - bitmap_left); gl::end(); gl::disable(GL_TEXTURE_2D); } /* * This function is called to draw on-screen target indicators * If is_active_target is set, the target is the currently selected HUD target * */ void HUD::draw_target(core::Entity *entity, bool is_active_target) { using math::Vector3f; // don't draw target if we're very close to it if (render::ext_render(entity)->distance() < 0.001f) { return; } // don't draw target if it is outside the visible cone Vector3f target(entity->location() - render::camera().location()); if (math::dotproduct(render::camera().axis().forward(), Vector3f::normalized(target)) < 0.75) { draw_offscreen_target(entity, is_active_target); return; } const core::EntityControlable *controlable = ( (entity->type() == core::Entity::Controlable) ? static_cast(entity) : 0 ); // calculate target screen position // transform the target into the camera coordinate system target = render::camera().axis().transpose() * target; // calculate the intersection between the line (0,0,0)-target and the frustum front float t = (render::FRUSTUMFRONT + 0.001f) / target.x(); Vector3f center(target * t); float cx = roundf(render::State::width() * (0.5 - center.y())); float cy = roundf(render::State::height() * (0.5 - center.z() * render::State::aspect())); if ((cx < 0) || (cy < 0) || (cx > render::State::width()) || (cy > render::State::height())) { draw_offscreen_target(entity, is_active_target); return; } // --------------------------------------------------------- // draw target bitmap float bitmap_radius = 32.0f; if (is_active_target) { bitmap_radius *= 2.0f; if (hud_splash_time > 0.0f) { bitmap_radius *= (core::application()->time() - hud_splash_time) / SPLASH_POPUP_DELAY; } } else if (controlable && (render::ext_render(controlable)->distance() > core::range::fxdistance)) { bitmap_radius *= 0.5f; } math::Vector2f bitmap_location(cx - bitmap_radius, cy - bitmap_radius); math::Vector2f bitmap_size(2.0f * bitmap_radius, 2.0f * bitmap_radius); math::Color bitmap_color; const float reputation = core::localplayer()->reputation(entity->faction()); std:: string bitmap_material; // default target color if (controlable) { if (controlable->owner()) { bitmap_material.assign("bitmaps/hud/target_controlable"); bitmap_color.assign(1.0f); // white } else { bitmap_material.assign("bitmaps/hud/target_default"); bitmap_color.assign(0.75f); // gray } } else if (entity->has_flag(core::Entity::Dockable)) { bitmap_material.assign("bitmaps/hud/target_dockable"); bitmap_color.assign(0.75f); // gray } else { bitmap_material.assign("bitmaps/hud/target_default"); bitmap_color.assign(palette()->text()); // default text color } // mission, autopilot and reputation override target color if (entity == core::localplayer()->mission_target()) { // mission target bitmap_color.assign(palette()->mission()); } else if (entity == core::localplayer()->autopilot_target()) { // mission target bitmap_color.assign(palette()->mission()); } else if (reputation >= core::range::reputation_friendly) { // friendly bitmap_color.assign(0.0f, 1.0f, 0.0f); // green } else if (reputation <= core::range::reputation_hostile) { // hostile bitmap_color.assign(1.0f, 0.0f, 0.0f); // red } ui::Paint::draw_bitmap(bitmap_location, bitmap_size, bitmap_color, bitmap_material); // --------------------------------------------------------- // draw target entity text labels bool do_draw_label = true; if (is_active_target) { // check if the splash animation is still running if (hud_splash_time > 0.0f) { do_draw_label = false; } } else if (controlable) { // do not show labels on far away NPCSs if (!controlable->owner() && (render::ext_render(controlable)->distance() > core::range::fxdistance)) { do_draw_label = false; } } if (do_draw_label) { // draw name label std::string label_color; std::string label_text; if (controlable && controlable->owner()) { label_color.assign("^B"); } else if (entity->has_flag(core::Entity::Dockable)) { label_color.assign("^B"); } else { label_color.assign("^N"); } label_text.assign(label_color); if (controlable && controlable->owner()) { label_text.append(controlable->owner()->name()); } else { label_text.append(entity->name()); } const ui::Font *label_font; if (is_active_target) { label_font = ui::root()->font_small(); } else { label_font = ui::root()->font_tiny(); } math::Vector2f label_size(2.0f * bitmap_radius, label_font->height()); math::Vector2f label_location(cx - bitmap_radius, cy - bitmap_radius -label_size.height()); ui::Paint::draw_label(label_location, label_size,label_font, label_text); // draw distance label if (is_active_target) { std::ostringstream strdistance; strdistance << label_color; float d = math::distance(core::localcontrol()->location(), entity->location()) - entity->radius() - core::localcontrol()->radius(); if (d > 0) { if (d > 100.0f) { strdistance << roundf(d * 0.1f) << "km"; } else { strdistance << roundf(d * 100.0f) << "m"; } } else { strdistance << "--"; } label_location.assign(cx - bitmap_radius, cy - bitmap_radius); ui::Paint::draw_label(label_location, label_size, label_font, strdistance.str()); } if (controlable && (is_active_target || (render::ext_render(controlable)->distance() < core::range::fxdistance))) { // health bar size const float hb_width = bitmap_radius; float hb_height = ui::root()->font_tiny()->width(); if (!is_active_target) { hb_height *= 0.5f; } const float hb_f = hb_width * controlable->health() / 100.0f; math::Vector2f pos; pos[0] = cx - bitmap_radius; pos[0] += bitmap_radius - 0.5f * hb_width; pos[1] = cy + bitmap_radius - 1.5f * hb_height; // health bar gl::color(0, 1, 0, 1); gl::begin(gl::Quads); gl::vertex(pos[0], pos[1]); gl::vertex(pos[0] + hb_f, pos[1]); gl::vertex(pos[0] + hb_f, pos[1] + hb_height); gl::vertex(pos[0], pos[1] + hb_height); gl::end(); // health bar frame gl::color(palette()->foreground()); gl::begin(gl::LineLoop); gl::vertex(pos[0], pos[1]); gl::vertex(pos[0] + hb_width, pos[1]); gl::vertex(pos[0] + hb_width, pos[1] + hb_height); gl::vertex(pos[0], pos[1] + hb_height); gl::end(); } } } bool HUD::on_keypress(const int key, const unsigned int modifier) { switch (key) { case SDLK_ESCAPE: if (targets::current()) { targets::reset(); } else { Client::show_menu("game"); } return true; } return false; } void HUD::draw() { using namespace render; if (core::localcontrol() && (input::mouse_control || input::joystick_control) && (render::camera().mode() != render::camera().Overview)) { hud_center->set_visible(true); } else { hud_center->set_visible(false); } gl::enable(GL_TEXTURE_2D); Text::setfont("gui", 12, 18); Text::setcolor('N'); //set normal color core::Zone *zone = core::localcontrol()->zone(); if (targets::current()) { if (targets::current()->id() != hud_last_target) { hud_splash_time = core::application()->time(); } else if (hud_splash_time + SPLASH_POPUP_DELAY < core::application()->time()) { hud_splash_time = 0.0f; } hud_last_target = targets::current()->id(); } else { hud_splash_time = 0.0f; hud_last_target = 0; } // draw HUD targets for (core::Zone::Content::iterator it = zone->content().begin(); it != zone->content().end(); it++) { core::Entity *entity = (*it); if (entity == targets::current()) { // draw current HUD target draw_target(entity, true); } else if (entity == core::localplayer()->autopilot_target()) { // draw current mission target draw_target(entity, false); } else if (entity == core::localplayer()->mission_target()) { // draw current mission target draw_target(entity, false); } else if ((entity->type() == core::Entity::Controlable) && (targets::is_valid_hud_target(entity))) { // draw other players draw_target(entity, false); } } gl::disable(GL_TEXTURE_2D); if (has_mouse_focus()) { if (render::camera().mode() == render::camera().Overview) { ui::root()->set_pointer("pointer", ui::Palette::Highlight); } else if (targets::hover()) { ui::root()->set_pointer("target", ui::Palette::Pointer, true); if (input::joystick_lastmoved_time() > input::mouse_lastmoved_time()) { ui::root()->input_mouse(render::State::width() / 2, render::State::height() / 2); } } else if (input::mouse_control) { ui::root()->set_pointer("control", ui::Palette::Pointer); } else if ((input::joystick_lastmoved_time() > input::mouse_lastmoved_time()) && (render::camera().mode() == render::camera().Cockpit || render::camera().mode() == render::camera().Track)) { ui::root()->set_pointer(); } else { ui::root()->set_pointer("aim", ui::Palette::Pointer); } } } }