/* game/ship.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 */ // project headers #include "auxiliary/functions.h" #include "core/gameserver.h" #include "core/entity.h" #include "game/game.h" #include "game/ship.h" #include "math/mathlib.h" // C++ headers #include using math::degrees360f; using math::degrees180f; namespace game { const float MIN_DELTA = 10e-10; Ship::Ship(core::Player *owner, ShipModel *shipmodel) : core::EntityControlable(owner, ship_enttype) { entity_modelname = "ships/" + shipmodel->modelname(); entity_name = shipmodel->name() + ": <^B" + owner->name() + "^N>"; entity_label = shipmodel->label(); entity_moduletypeid = ship_enttype; current_target_direction = 0.0f; current_target_pitch = 0.0f;; current_target_roll = 0.0f; entity_color = owner->color(); entity_color_second = owner->color_second(); ship_shipmodel = shipmodel; ship_jumpdrive = shipmodel->shipmodel_jumpdrive; ship_jumptargetzone = 0; ship_impulsedrive_timer = 0; ship_jumpdrive_timer = 0; } Ship::~Ship() { } void Ship::impulse() { if (entity_eventstate == core::Entity::Jump) { return; } else if ((entity_eventstate == core::Entity::Impulse) || (entity_eventstate == core::Entity::ImpulseInitiate)) { entity_eventstate = core::Entity::Normal; } else { if (entity_eventstate == core::Entity::JumpInitiate) { ship_jumpdrive_timer = 0; ship_jumptargetzone = 0; entity_timer = 0; } entity_eventstate = core::Entity::ImpulseInitiate; if (Game::instance()->g_devel->value()) { entity_timer = 0; } else { entity_timer = 10; } ship_impulsedrive_timer = core::server()->time(); entity_dirty = true; } entity_dirty = true; } void Ship::jump(std::string const &args) { if (!jumpdrive() && !Game::instance()->g_devel->value()) { core::server()->send(owner(), "This ship is not equiped with a hyperspace drive!"); return; } else if (entity_eventstate == core::Entity::Jump) { return; } else if (entity_eventstate == core::Entity::JumpInitiate) { core::server()->send(owner(), "Jump aborted, hyperspace drive deactivated."); ship_jumptargetzone = 0; ship_jumpdrive_timer = 0; entity_timer = 0; entity_eventstate = core::Entity::Normal; return; } std::string target; std::istringstream is(args); if (!(is >> target)) { std::string helpstr; for (core::Zone::Registry::iterator it = core::Zone::registry().begin(); it != core::Zone::registry().end(); it++) { core::Zone *zone = (*it).second; if (helpstr.size()) helpstr.append("^N|^B"); helpstr.append(zone->label()); } core::server()->send(owner(), "Usage: jump [^B" + helpstr + "^N]"); return; } aux::to_lowercase(target); ship_jumptargetzone = core::Zone::find_zone(target); if (!ship_jumptargetzone) { core::server()->send(owner(), "Unknown system '" + target + '\''); return; } if (ship_jumptargetzone == zone()) { core::server()->send(owner(), "Already in the " + ship_jumptargetzone->name() + '.'); ship_jumptargetzone = 0; return; } entity_eventstate = core::Entity::JumpInitiate; if (Game::instance()->g_devel->value()) { entity_timer = 0; } else { entity_timer = 10; } ship_jumpdrive_timer = core::server()->time(); entity_dirty = true; } void Ship::frame(float seconds) { const float direction_change_speed = 2; float cosangle; // cosine of an angle float angle; // angle in radians math::Vector3f n; // normal of a plane float actual_maxspeed = ship_shipmodel->maxspeed(); float actual_turnspeed = ship_shipmodel->turnspeed(); float actual_acceleration = ship_shipmodel->acceleration(); // speed might get set to 0 on this update if (entity_speed != 0.0f) entity_dirty = true; // jumpdrive // target axis math::Axis target_axis(entity_axis); if (entity_eventstate == core::Entity::JumpInitiate) { if (ship_jumpdrive_timer + 1.0f <= core::server()->time()) { entity_timer -= 1.0f; if (entity_timer <= 0) { core::server()->send(owner(), "Jumping to '" + ship_jumptargetzone->name() + '\''); set_zone(ship_jumptargetzone); if (owner()->control() == (EntityControlable*) this) owner()->set_zone(ship_jumptargetzone); ship_jumpdrive_timer = 0; ship_jumptargetzone = 0; entity_timer = 0; entity_eventstate = core::Entity::Jump; entity_dirty = true; return; } else { ship_jumpdrive_timer = core::server()->time(); entity_dirty = true; } } // control is disabled while the jumpdrive is activated target_thrust = 0; target_pitch = 0; target_roll = 0; target_direction = 0; } else if (entity_eventstate == core::Entity::Jump) { // FIXME jump location and axis math::Axis default_axis; entity_axis.assign(default_axis); entity_dirty = true; // FIXME 5 second cooldown entity_eventstate = core::Entity::Normal; } else if (entity_eventstate == core::Entity::ImpulseInitiate) { if (ship_impulsedrive_timer + 1.0f <= core::server()->time()) { entity_timer -= 1.0f; if (entity_timer <= 0) { actual_maxspeed = Game::instance()->g_impulsespeed->value(); actual_acceleration = Game::instance()->g_impulseacceleration->value(); entity_eventstate = core::Entity::Impulse; entity_timer = 0; entity_dirty = true; } else { ship_impulsedrive_timer = core::server()->time(); entity_dirty = true; } } // clamp input values target_thrust = 1.0f; math::clamp(target_pitch, -1.0f, 1.0f); math::clamp(target_roll, -1.0f, 1.0f); math::clamp(target_direction, -1.0f, 1.0f); actual_turnspeed *= 0.5; } else if (entity_eventstate == core::Entity::Impulse) { // clamp input values target_thrust = 1.0f; math::clamp(target_pitch, -1.0f, 1.0f); math::clamp(target_roll, -1.0f, 1.0f); math::clamp(target_direction, -1.0f, 1.0f); actual_maxspeed = Game::instance()->g_impulsespeed->value(); actual_acceleration = Game::instance()->g_impulseacceleration->value(); actual_turnspeed *= 0.5; } else if (entity_eventstate == core::Entity::Normal) { // clamp input values math::clamp(target_thrust, 0.0f, 1.0f); math::clamp(target_pitch, -1.0f, 1.0f); math::clamp(target_roll, -1.0f, 1.0f); math::clamp(target_direction, -1.0f, 1.0f); if (speed() > actual_maxspeed) { actual_acceleration = Game::instance()->g_impulseacceleration->value(); actual_turnspeed *= 0.5; } } // update thrust if (entity_thrust < target_thrust) { entity_thrust += seconds * 0.5f; if (entity_thrust > target_thrust) entity_thrust = target_thrust; } else if (entity_thrust > target_thrust) { entity_thrust -= seconds * 0.5f; if (entity_thrust < target_thrust) entity_thrust = target_thrust; } //math::clamp(entity_thrust, 0.0f, 1.0f); // update roll if (current_target_roll < target_roll) { current_target_roll += direction_change_speed * seconds; if (current_target_roll > target_roll) current_target_roll = target_roll; } else if (current_target_roll > target_roll) { current_target_roll -= direction_change_speed * seconds; if (current_target_roll < target_roll) current_target_roll = target_roll; } math::clamp(current_target_roll, -1.0f, 1.0f); if (fabs(current_target_roll) > MIN_DELTA) { float roll_offset = seconds * current_target_roll; entity_axis.change_roll(actual_turnspeed * roll_offset); } else { current_target_roll = 0.0f; } // update target_axis direction if (current_target_direction < target_direction) { current_target_direction += direction_change_speed * seconds; if (current_target_direction > target_direction) { current_target_direction = target_direction; } } else if (current_target_direction > target_direction) { current_target_direction -= direction_change_speed * seconds; if (current_target_direction < target_direction) { current_target_direction = target_direction; } } if (fabs(current_target_direction) > MIN_DELTA ) { math::clamp(current_target_direction, -1.0f, 1.0f); target_axis.change_direction(actual_turnspeed * current_target_direction); } else { current_target_direction = 0.0f; } if (current_target_pitch < target_pitch) { current_target_pitch += direction_change_speed * seconds; if (current_target_pitch > target_pitch) current_target_pitch = target_pitch; } else if (current_target_pitch > target_pitch) { current_target_pitch -= direction_change_speed * seconds; if (current_target_pitch < target_pitch) current_target_pitch = target_pitch; } if (fabs(current_target_pitch) > MIN_DELTA) { math::clamp(current_target_pitch, -1.0f, 1.0f); target_axis.change_pitch(actual_turnspeed * current_target_pitch); } else { current_target_pitch = 0.0f; } n.assign(math::crossproduct(entity_axis.forward(), target_axis.forward())); if (!(n.length() < MIN_DELTA)) { n.normalize(); cosangle = math::dotproduct(entity_axis.forward(), target_axis.forward()); angle = acos(cosangle) * seconds; // * 180.0f / M_PI; if (angle > MIN_DELTA) entity_axis.rotate(n, -angle); } // update speed if (entity_speed < entity_thrust * actual_maxspeed) { entity_speed += actual_acceleration * seconds; if (entity_speed > entity_thrust * actual_maxspeed) { entity_speed = entity_thrust * actual_maxspeed; } } else if(entity_speed > entity_thrust * actual_maxspeed) { entity_speed -= actual_acceleration * seconds; if (entity_speed < 0.0f) entity_speed = 0.0f; } if (entity_speed != 0.0f) { entity_location += entity_axis.forward() * entity_speed * seconds; entity_dirty = true; } else if ((current_target_pitch != 0.0f) || (current_target_direction != 0.0f) || (current_target_roll != 0.0f)) { entity_dirty = true; } } } // namespace game