/* 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/sources.h" #include "client/input.h" #include "client/view.h" #include "client/video.h" #include "core/application.h" #include "core/entity.h" #include "core/func.h" #include "core/gameinterface.h" #include "math/vector3f.h" #include "render/gl.h" #include "render/render.h" #include "render/text.h" namespace client { namespace targets { const float TARGETBOXRADIUS = 0.025f; unsigned int current_target_id = 0; unsigned int current_hover = 0; core::Entity *current_target = 0; core::Cvar *snd_engines = 0; bool is_legal_target(core::Entity *entity) { if (entity->id() == core::localcontrol()->id()) return false; else return true; } core::Entity* current() { return current_target; } unsigned int current_id() { return current_target_id; } unsigned int hover() { return current_hover; } void select_target(core::Entity *entity) { current_target = entity; current_target_id = current_target->id(); core::application()->notify_sound("ui/target"); } void select_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) select_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_legal_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_legal_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()) { select_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_legal_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_legal_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()) { select_target((*rit)); } else { current_target = 0; current_target_id = 0; } } void reset() { current_target = 0; current_target_id = 0; current_hover = 0; } void init() { snd_engines = core::Cvar::get("snd_engines", "1", core::Cvar::Archive); snd_engines->set_info("[bool] enable or disable engine sounds"); 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"); reset(); } void shutdown() { reset(); core::Func::remove("target_next"); core::Func::remove("target_prev"); } void render_listener_sound() { if (!(snd_engines && snd_engines->value())) return; math::Vector3f velocity(0, 0 ,0); if (core::localcontrol()) { velocity.assign(core::localcontrol()->state()->axis().forward() * core::localcontrol()->speed()); } audio::update_listener(render::Camera::eye(), render::Camera::axis(), velocity); } void render_entity_sound(core::Entity *entity) { if (!(entity->type() == core::Entity::Controlable)) return; if (!(snd_engines && snd_engines->value())) { entity->state()->clearsound(); return; } core::EntityControlable *entitycontrolable = (core::EntityControlable *) entity; core::ClientState *state = entity->state(); if (entity->model() && state->detailvisible() && entitycontrolable->thrust() > 0 ) { float speed = entitycontrolable->speed(); float pitch = 0.2f + entitycontrolable->thrust() * 0.8f; if (!state->state_enginesound) { if ((state->state_enginesound = audio::Sources::get()) > 0 ) { size_t enginesound = 0; if (entity->model()) enginesound = entity->model()->enginesound(); std::stringstream soundname; soundname << "engines/loop" << std::setfill('0') << std::setw(2) << enginesound; audio::loop(state->state_enginesound, soundname.str().c_str(), pitch, 0); } } if (state->state_enginesound) { audio::update_source(state->state_enginesound, state->location() - state->axis().forward() * entity->model()->maxbbox().y , state->axis().forward() * speed, pitch); } } else { entity->state()->clearsound(); } } // render client targets 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 = (float)(input::mouse_position_x() - video::width /2) / (float)video::width; float y = (float)(input::mouse_position_y() - video::height /2) / (float)video::height / render::Camera::aspect(); Vector3f cursor = render::Camera::eye() + render::Camera::axis().forward() * (render::Camera::frustum_front() + 0.001); cursor -= render::Camera::axis().left() * x; cursor -= render::Camera::axis().up() * y; math::Vector3f center = render::Camera::eye() + (render::Camera::axis().forward() * (render::Camera::frustum_front() +0.01f)); for (core::Zone::Content::iterator it=zone->content().begin(); it != zone->content().end(); it++) { core::Entity *entity = (*it); // render entity sound render_entity_sound(entity); // find the current target if (entity->id() == current_target_id) { current_target = entity; } // check if the mouse is hovering the entity if (core::localcontrol() && entity->state()->visible()) { Vector3f v(entity->state()->location() - render::Camera::eye()); v.normalize(); if (is_legal_target(entity) && (math::dotproduct(render::Camera::axis().forward(), v) > 0.75 )) { // caculate 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()); // if the cursor-beam hits the entity sphere if (d < entity->radius() * 0.5f) { 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(); } } void draw_target() { using math::Vector3f; if (!current_target) return; core::Entity *entity = current_target; // if target is in visible space Vector3f v(entity->state()->location() - render::Camera::eye()); v.normalize(); if (math::dotproduct(render::Camera::axis().forward(), v) > 0.75 ) { // find the intersection of the line [ eye - target ] in the frustum front plane Vector3f const &plane_normal = render::Camera::axis().forward(); Vector3f const &plane_point = render::Camera::eye() + render::Camera::axis().forward() * (render::Camera::frustum_front() + 0.001); float d = (plane_normal.x * plane_point.x + plane_normal.y * plane_point.y + plane_normal.z * plane_point.z); float t = d - plane_normal.x * render::Camera::eye().x - plane_normal.y * render::Camera::eye().y - plane_normal.z * render::Camera::eye().z; t /= plane_normal.x * (entity->state()->location().x - render::Camera::eye().x) + plane_normal.y * (entity->state()->location().y - render::Camera::eye().y) + plane_normal.z * (entity->state()->location().z - render::Camera::eye().z); Vector3f intersection = render::Camera::eye() + (entity->state()->location() - render::Camera::eye()) * t; // draw the target cursor in the frustum front plane, but in real world coordinates const float r = 0.05; render::gl::color(1, 1, 1, 1); render::gl::begin(render::gl::LineLoop); render::gl::vertex(intersection + render::Camera::axis().up() * r); render::gl::vertex(intersection + render::Camera::axis().left() * r); render::gl::vertex(intersection - render::Camera::axis().up() * r); render::gl::vertex(intersection - render::Camera::axis().left() * r); render::gl::end(); } } } }