/* client/targets.cc This file is part of the Osirion project and is distributed under the terms of the GNU General Public License version 2 */ #include #include #include #include #include "audio/audio.h" #include "audio/buffers.h" #include "audio/sources.h" #include "auxiliary/functions.h" #include "audio/sources.h" #include "client/client.h" #include "client/input.h" #include "client/soundext.h" #include "client/video.h" #include "core/application.h" #include "core/entity.h" #include "core/func.h" #include "core/gameinterface.h" #include "core/range.h" #include "math/axis.h" #include "math/vector3f.h" #include "render/camera.h" #include "render/state.h" namespace client { namespace targets { const float TARGETBOXRADIUS = 0.025f; unsigned int current_target_id = 0; unsigned int current_hover = 0; const core::Entity *current_target = 0; bool is_valid_map_target(const core::Entity *entity) { if (entity->serverside()) { return false; } else if (entity == core::localcontrol()) { return false; } else if (entity->has_flag(core::Entity::ShowOnMap)) { return true; } else if (entity == core::localplayer()->mission_target()) { return true; } else { return false; } } bool is_valid_hud_target(const core::Entity *entity) { if (entity->serverside()) { return false; } else if (entity->type() == core::Entity::Projectile) { return false; } else if (entity == core::localcontrol()) { return false; } else if (entity->has_flag(core::Entity::ShowOnMap)) { return true; } else if (entity == core::localplayer()->mission_target()) { return true; } else if (!ext_render(entity)) { return false; } else { return ext_render(entity)->visible(); } } const core::Entity* current() { return current_target; } unsigned int current_id() { return current_target_id; } unsigned int hover() { return current_hover; } void set_target(const core::Entity *entity) { current_target = entity; if (entity) { current_target_id = current_target->id(); audio::play("ui/target"); } else { current_target_id = 0; } } void set_target(unsigned int id) { if (!core::localcontrol()) return; core::Zone *zone = core::localcontrol()->zone(); if (!zone) return; core::Entity *entity = zone->find_entity(id); if (entity && is_valid_hud_target(entity)) set_target(entity); } void func_target_next(std::string const &args) { if (!core::localcontrol()) return; core::Zone *zone = core::localcontrol()->zone(); if (!zone) return; if (!zone->content().size()) { current_target = 0; current_target_id = 0; return; } core::Zone::Content::iterator it = zone->content().begin(); if (!current_target_id) { // first entity it = zone->content().begin(); while (!is_valid_hud_target((*it)) && it != zone->content().end()) it++; } else { // current entity while (it != zone->content().end() && ((*it)->id() != current_target_id)) ++it; // next legal entity if (it != zone->content().end()) it++; if (it == zone->content().end()) { it = zone->content().begin(); } while (!is_valid_hud_target((*it))) { it++; if (it == zone->content().end()) it = zone->content().begin(); if ((*it)->id() == current_target_id) { current_target = (*it); return; } } } if (it != zone->content().end()) { set_target((*it)); } else { current_target = 0; current_target_id = 0; } } void func_target_prev(std::string const &args) { if (!core::localcontrol()) return; core::Zone *zone = core::localcontrol()->zone(); if (!zone) return; if (!zone->content().size()) { current_target = 0; current_target_id = 0; return; } core::Zone::Content::reverse_iterator rit = zone->content().rbegin(); if (!current_target_id) { // last entity rit = zone->content().rbegin(); while (!is_valid_hud_target((*rit)) && rit != zone->content().rend()) rit++; } else { // current entity while (rit != zone->content().rend() && ((*rit)->id() != current_target_id)) ++rit; // previous legal entity if (rit != zone->content().rend()) ++rit; if (rit == zone->content().rend()) { rit = zone->content().rbegin(); } while (!is_valid_hud_target((*rit))) { ++rit; if (rit == zone->content().rend()) rit = zone->content().rbegin(); if ((*rit)->id() == current_target_id) { current_target = (*rit); return; } } } if (rit != zone->content().rend()) { set_target((*rit)); } else { current_target = 0; current_target_id = 0; } } void func_target_none(std::string const &args) { current_target = 0; current_target_id = 0; } void func_target_center(std::string const &args) { if (!core::localcontrol()) return; // this is essentialy the hover algorithm with the cursor in the center const core::Entity *new_target = 0; math::Vector3f center = render::Camera::eye() + render::Camera::axis().forward() * (render::FRUSTUMFRONT + 0.001); float smallest_d = -1; for (core::Zone::Content::const_iterator it = core::localcontrol()->zone()->content().begin(); it != core::localcontrol()->zone()->content().end(); it++) { const core::Entity *entity = (*it); math::Vector3f v(entity->location() - render::Camera::eye()); v.normalize(); if (is_valid_hud_target(entity) && math::dotproduct(render::Camera::axis().forward(), v) > 0.85) { // calculate the distance from entity location to the line [eye - cursor] float d = math::Vector3f::length(math::crossproduct((center - render::Camera::eye()) , (render::Camera::eye() - entity->location()))) / math::Vector3f::length(center - render::Camera::eye()); // the entity closer to the center beam if (smallest_d < 0 || d < smallest_d) { new_target = entity; smallest_d = d; } } } if (new_target) set_target(new_target); } void reset() { current_target = 0; current_target_id = 0; current_hover = 0; } void init() { core::Func *func = 0; func = core::Func::add("target_next", func_target_next); func->set_info("select next target"); core::Func::add("target_prev", func_target_prev); func->set_info("select previous target"); core::Func::add("target_none", func_target_none); func->set_info("deselect current target"); core::Func::add("target_center", func_target_center); func->set_info("select target near center"); reset(); } void shutdown() { reset(); core::Func::remove("target_next"); core::Func::remove("target_prev"); core::Func::remove("target_none"); core::Func::remove("target_center"); } // render targets and sounds (in world coordinates) void frame() { core::Zone *zone = core::localplayer()->zone(); if (!zone) return; /* Notes http://en.wikipedia.org/wiki/Line-plane_intersection http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html */ using math::Vector3f; render_listener_sound(); current_target = 0; current_hover = 0; float z = -1; // mouse cursor location in 3d world space float x = 0; float y = 0; if ((input::joystick_lastmoved_time() > input::mouse_lastmoved_time()) && (render::Camera::mode() == render::Camera::Cockpit || render::Camera::mode() == render::Camera::Track)) { x = 0; y = 0; } else { x = (float)(input::mouse_position_x() - render::State::width() / 2) / (float)render::State::width(); y = (float)(input::mouse_position_y() - render::State::height() / 2) / (float)render::State::height() / render::State::aspect(); } Vector3f cursor = render::Camera::eye() + render::Camera::axis().forward() * (render::FRUSTUMFRONT + 0.001); cursor -= render::Camera::axis().left() * x; cursor -= render::Camera::axis().up() * y; // set aim if (core::localcontrol()) { core::localcontrol()->set_target_aim(cursor); } //math::Vector3f center = render::Camera::eye() + (render::Camera::axis().forward() * (render::FRUSTUMFRONT + 0.001f)); for (core::Zone::Content::iterator it = zone->content().begin(); it != zone->content().end(); it++) { core::Entity *entity = (*it); // render entity sound if ((entity->type() == core::Entity::Controlable) || (entity->model() && entity->model()->sounds().size())) { render_entity_sound(entity); } // find the current target if (!core::localplayer()->view() && core::localcontrol() && (is_valid_hud_target(entity) || is_valid_map_target(entity))) { if (entity->id() == current_target_id) { current_target = entity; } // check if the mouse is hovering the entity Vector3f v(entity->location() - render::Camera::eye()); v.normalize(); if (math::dotproduct(render::Camera::axis().forward(), v) > 0.75) { // calculate the distance from entity location to the line [eye - cursor] float d = math::Vector3f::length(math::crossproduct((cursor - render::Camera::eye()) , (render::Camera::eye() - entity->location()))) / math::Vector3f::length(cursor - render::Camera::eye()); float r = entity->radius() * 0.5f; if (ext_render(entity)->distance() > 512.0f) math::clamp(r, 8.0f, r); else if (ext_render(entity)->distance() > 256.0f) math::clamp(r, 4.0f, r); else if (ext_render(entity)->distance() > 128.0f) math::clamp(r, 2.0f, r); // if the cursor-beam hits the entity sphere if (d < r) { float myz = math::distance(cursor, entity->location()); if (z < 0 || myz < z) { current_hover = entity->id(); z = myz; } } } } } if (!current_target) { current_target_id = 0; } else { current_target_id = current_target->id(); } } } }