/* base/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 */ #include #include #include "auxiliary/functions.h" #include "core/gameserver.h" #include "core/entity.h" #include "base/game.h" #include "base/ship.h" #include "math/functions.h" using math::degrees360f; using math::degrees180f; namespace game { const float MIN_DELTA = 0.000001f; Ship::Ship(core::Player *owner, const ShipModel *shipmodel) : core::EntityControlable() { assert(shipmodel); entity_moduletypeid = ship_enttype; set_radius(0); ship_shipmodel = shipmodel; ship_impulsedrive_timer = 0; ship_jumpdrive_timer = 0; ship_jumpdepart = 0; ship_dock = 0; ship_spawn = 0; current_impulse = false; // apply ship type settings ship_shipmodel->apply(this); // radius fallback values if (!radius()) { if (model()) { set_radius(model()->radius()); } } if (!radius()) { set_radius(0.5f); } // mass fallback value if (!mass()) { set_mass(radius() * 100.0f); } if (owner) { // this ship is owned by a player, // player colors override template colors set_owner(owner); get_color().assign(owner->color()); get_color_second().assign(owner->color_second()); std::string str(aux::text_strip(owner->name())); aux::to_label(str); set_label(str); // add an inventory add_inventory(); inventory()->set_capacity(ship_shipmodel->maxcargo()); } if (model()) { add_slots(); slots()->load(model()); } // menus for docked players if (ship_shipmodel->dockable()) { using core::MenuDescription; using core::ButtonDescription; MenuDescription *menu_main = new MenuDescription(); menu_main->set_label("main"); menu_main->set_text("Launch area"); add_menu(menu_main); ButtonDescription *button = new ButtonDescription(); button->set_text("Launch"); button->set_command("launch", ButtonDescription::CommandGame); button->set_alignment(ButtonDescription::Center); menu_main->add_button(button); set_flag(core::Entity::Dockable); } // initialize physics // FIXME probably should not be called here //reset(); //body()->setDamping(ship_shipmodel->linear_damping(), ship_shipmodel->angular_damping()); } Ship::~Ship() { } void Ship::func_impulse() { switch (entity_state) { case core::Entity::Impulse: entity_state = core::Entity::Normal; //target_thrust = 1.0f; //entity_thrust = 0.0f; set_dirty(); break; case core::Entity::ImpulseInitiate: entity_state = core::Entity::Normal; set_dirty(); break; case core::Entity::JumpInitiate: entity_state = core::Entity::Normal; ship_jumpdrive_timer = 0; entity_timer = 0; set_dirty(); break; case core::Entity::Normal: entity_state = core::Entity::ImpulseInitiate; entity_timer = impulse_timer_delay; ship_impulsedrive_timer = core::server()->time(); break; default: break; } } void Ship::initiate_jump(JumpPoint *depart) { ship_jumpdepart = 0; if (!depart) return; if (!depart->target()) return; ship_jumpdepart = depart; entity_state = core::Entity::JumpInitiate; entity_timer = jump_timer_delay; ship_jumpdrive_timer = core::server()->time(); set_dirty(); } void Ship::func_jump(std::string const &args) { if (entity_state == core::Entity::Docked) { return; } // devel mode provides instant jump access to arbitrary systems if (Game::g_devel->value() && (args.size())) { core::Zone *jumptargetzone = core::Zone::search(args); if (!jumptargetzone) { std::string helpstr; for (core::Zone::Registry::iterator it = core::Zone::registry().begin(); it != core::Zone::registry().end(); it++) { if (helpstr.size()) helpstr.append("^N|^B"); helpstr.append((*it).second->label()); } owner()->send("Usage: jump [^B" + helpstr + "^N]"); return; } if (jumptargetzone == zone()) { owner()->send("Already in the " + jumptargetzone->name()); return; } owner()->send("Jumping to the " + jumptargetzone->name()); set_zone(jumptargetzone); //ship_jumpdrive_timer = 0; //entity_timer = 0; // setting the cooldown timer even with cheats on will allow some time for the jump completed sound to play ship_jumpdrive_timer = core::server()->time(); entity_timer = jump_cooldown_delay; set_state(core::Entity::Jump); set_dirty(); return; } else { if (!jumpdrive() && !Game::g_devel->value()) { owner()->send("This ship is not equiped with a hyperspace drive!"); return; } else if (entity_state == core::Entity::Jump) { return; } else if (entity_state == core::Entity::JumpInitiate) { owner()->send("Jump aborted, hyperspace drive deactivated"); ship_jumpdrive_timer = 0; entity_timer = 0; entity_state = core::Entity::Normal; return; } initiate_jump(find_closest_jumppoint()); } } JumpPoint * Ship::find_closest_jumppoint() { // find closest jumpgate or jumppoint float d = -1; JumpPoint *jumppoint = 0; for (core::Zone::Content::iterator it = zone()->content().begin(); it != zone()->content().end(); it++) { core::Entity *entity = (*it); if ((entity->moduletype() == jumppoint_enttype) || (entity->moduletype() == jumpgate_enttype)) { JumpPoint *te = static_cast(entity); float d1 = math::distance(location(), te->location()); if ((d < 0) || (d1 < d)) { d = d1; jumppoint = te; } } } if (jumppoint && jumppoint->target()) { if (Game::g_jumppointrange->value() < d) { owner()->send("Jumppoint out of range!"); return 0; } else { owner()->send("Jumping to the " + jumppoint->target()->zone()->name()); return jumppoint; } } else { owner()->send("No jumppoints found!"); return 0; } return 0; } void Ship::explode() { set_state(core::Entity::Destroyed); target_direction = 0; target_pitch = 0; target_roll = 0; target_strafe = 0.0f; target_vstrafe = 0.0f; target_afterburner = 0.0f; target_thrust = 0; entity_thrust = 0; if (owner()) { if (owner()->control() == this) owner()->set_view(this); } }; void Ship::reset() { current_target_direction = 0.0f; current_target_pitch = 0.0f;; current_target_roll = 0.0f; current_target_strafe = 0.0f; current_target_vstrafe = 0.0f; current_target_afterburner = 0.0f; EntityControlable::reset(); if (body()) { body()->setDamping(ship_shipmodel->linear_damping(), ship_shipmodel->angular_damping()); } } void Ship::set_zone(core::Zone *zone) { core::EntityControlable::set_zone(zone); if (owner() && (owner()->control() == (EntityControlable*) this)) owner()->set_zone(zone); } void Ship::set_dock(core::Entity *dock) { if (!dock) { ship_dock = 0; return; } get_location().assign(dock->location()); get_axis().assign(dock->axis()); ship_dock = dock; set_state(core::Entity::Docked); reset(); // if the dock is not owned by a player. set it as spawn const core::Player *owner = (dock->type() == core::Entity::Controlable ? static_cast(dock)->owner() : 0 ); if (!owner) { set_spawn(dock); } } void Ship::launch() { if (!ship_dock) return; get_axis().assign(ship_dock->axis()); if (ship_dock->type() == core::Entity::Globe) get_location().assign(ship_dock->location() + (ship_dock->axis().forward() * (planet_safe_distance + this->radius() + ship_dock->radius()))); else get_location().assign(ship_dock->location() + (ship_dock->axis().forward() * (this->radius() + ship_dock->radius()))); ship_dock = 0; set_state(core::Entity::Normal); reset(); } void Ship::set_spawn(core::Entity *spawn) { ship_spawn = spawn; } void Ship::action (btScalar seconds) { //float maxspeed = 0; float engine_force = 0; float turn_force = ship_turn_force; float roll_force = ship_roll_force; btTransform t; switch (entity_state) { case core::Entity::Normal: engine_force = ship_thrust_force * entity_thrust; //maxspeed = ship_shipmodel->maxspeed() * entity_thrust; break; case core::Entity::ImpulseInitiate: case core::Entity::Impulse: engine_force = ship_impulse_force; //maxspeed = (Game::g_impulsespeed ? Game::g_impulsespeed->value() * 0.01f : 0.0f); turn_force *= .5f; roll_force *= .5f; break; case core::Entity::JumpInitiate: case core::Entity::Jump: //maxspeed = 0.0f; engine_force = 0.0f; break; case core::Entity::Docked: return; break; default: //maxspeed = 0.0f; engine_force = 0.0f; break; } t.setIdentity(); entity_motionstate->getWorldTransform(t); get_location().assign(t.getOrigin()); get_axis().assign(t.getBasis()); const float torque_scale = 0.001f; // apply strafe body()->applyCentralImpulse(math::to_btVector3(axis().up() * current_target_vstrafe * ship_strafe_force)); body()->applyCentralImpulse(math::to_btVector3(axis().left() * current_target_strafe * ship_strafe_force)); // apply afterburner body()->applyCentralImpulse(math::to_btVector3(axis().forward() * current_target_afterburner * ship_strafe_force)); // apply main thruster if (current_target_afterburner >= 0) { body()->applyCentralImpulse(math::to_btVector3(axis().forward() * engine_force)); } // apply direction body()->applyTorqueImpulse(math::to_btVector3(axis().up() * current_target_direction * turn_force * torque_scale)); // apply pitch body()->applyTorqueImpulse(math::to_btVector3(axis().left() * -current_target_pitch * turn_force * torque_scale)); // apply roll body()->applyTorqueImpulse(math::to_btVector3(axis().forward() * -current_target_roll * roll_force* torque_scale)); // limit speed entity_speed = (float) entity_body->getLinearVelocity().length(); if (entity_speed > Game::g_impulsespeed->value() * 0.01f) { entity_speed = Game::g_impulsespeed->value() * 0.01f; body()->setLinearVelocity(math::to_btVector3(axis().forward() * entity_speed)); } } void Ship::collision(core::Entity *other) { if (state() == core::Entity::Destroyed) { return; } if (state() == core::Entity::Docked) { return; } // do not fly into planets if (other->moduletype() == planet_enttype) { explode(); if (owner()) { std::string message("^B"); message.append(owner()->name()); message.append(" ^Bran into "); message.append(other->name()); message.append("."); core::server()->broadcast(message); } else { die(); } } else if (other->moduletype() == star_enttype) { explode(); if (owner()) { std::string message("^B"); message.append(owner()->name()); message.append(" ^Bcould not resist the light."); core::server()->broadcast(message); } else { die(); } } } void Ship::frame(const unsigned long elapsed) { //const float direction_reaction = 2.0f; // directional control reaction time //const float thrust_reaction = 0.5f; // thrust control reaction time //const float strafe_reaction = 1.5f; // strafe control reaction time //const float afterburner_reaction = 1.0f; // afterburner control reaction time math::Vector3f n; // normal of a plane math::Axis target_axis(axis()); // target axis /* -- update state ----------------------------------------- */ // speed might be set to 0 on this update if (entity_speed != 0.0f) { set_dirty(); } if (entity_state == core::Entity::Docked) { target_direction = 0; target_pitch = 0; target_roll = 0; target_strafe = 0.0f; target_vstrafe = 0.0f; target_afterburner = 0.0f; target_thrust = 0; entity_thrust = 0; entity_speed = 0.0f; entity_controlflags = 0; } else if (entity_state == core::Entity::JumpInitiate) { if (ship_jumpdrive_timer + 1.0f <= core::server()->time()) { entity_timer -= 1.0f; if (entity_timer <= 0) { if (ship_jumpdepart && ship_jumpdepart->target()) { set_state(core::Entity::Jump); entity_speed = Game::g_impulsespeed->value() * 0.01f; if (ship_jumpdepart->moduletype() == jumpgate_enttype) { get_axis().assign(ship_jumpdepart->target()->axis()); get_location().assign(ship_jumpdepart->target()->location()); get_location() += axis().forward() * (radius() + ship_jumpdepart->target()->radius()); } else { get_location().assign(ship_jumpdepart->target()->location() + location() - ship_jumpdepart->location()); } set_zone(ship_jumpdepart->target()->zone()); if (owner() && owner()->view() && (owner()->view()->zone() != ship_jumpdepart->target()->zone())) owner()->set_view(0); owner()->send("^BJumping to the " + ship_jumpdepart->target()->zone()->name()); } else { set_state(core::Entity::Normal); } ship_jumpdrive_timer = core::server()->time(); entity_timer = jump_cooldown_delay; set_dirty(); return; } else { ship_jumpdrive_timer = core::server()->time(); set_dirty(); } } // control is disabled while the jumpdrive is activated target_direction = 0; target_pitch = 0; target_roll = 0; target_strafe = 0.0f; target_vstrafe = 0.0f; target_afterburner = 0.0f; target_thrust = 0.1f; entity_controlflags = 0; } else if (entity_state == core::Entity::Jump) { // control is disabled while the jumpdrive is activated target_direction = 0; target_pitch = 0; target_roll = 0; target_strafe = 0.0f; target_vstrafe = 0.0f; target_afterburner = 0.0f; target_thrust = 0.0f; entity_controlflags = 0; // apply jump drive cooldown if (ship_jumpdrive_timer + 1.0f <= core::server()->time()) { entity_timer -= 1.0f; if (entity_timer <= 0) { ship_jumpdrive_timer = 0; set_state(core::Entity::Normal); set_dirty(); if (owner() && owner()->view() && owner()->control() == (EntityControlable*) this) owner()->set_view(0); } else { ship_jumpdrive_timer = core::server()->time(); set_dirty(); } } return; } else if (entity_state == core::Entity::ImpulseInitiate) { // cancel impulse drive if afterburner goes reverse if (target_afterburner < 0.0f) { set_state(core::Entity::Normal); set_dirty(); entity_timer = 0; } else { if (ship_impulsedrive_timer + 1.0f <= core::server()->time()) { entity_timer -= 1.0f; if (entity_timer <= 0) { entity_state = core::Entity::Impulse; entity_timer = 0; set_dirty(); } else { ship_impulsedrive_timer = core::server()->time(); set_dirty(); } } // clamp input values //target_thrust = 0.0f; //target_afterburner = 0.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); } } else if (entity_state == core::Entity::Impulse) { // clamp input values math::clamp(target_pitch, -1.0f, 1.0f); math::clamp(target_roll, -1.0f, 1.0f); math::clamp(target_direction, -1.0f, 1.0f); // cancel impulse drive if afterburner goes reverse if (target_afterburner < 0.0f) { set_state(core::Entity::Normal); set_dirty(); entity_timer = 0; //target_thrust = 1.0f; //entity_thrust = 1.0f; } else { //target_afterburner = 0.0f; //target_thrust = 0.0f; } } else if (entity_state == 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); math::clamp(target_afterburner, -1.0f, 1.0f); entity_controlflags = target_controlflags; } else if (entity_state == core::Entity::Destroyed) { target_direction = 0; target_pitch = 0; target_roll = 0; target_strafe = 0.0f; target_vstrafe = 0.0f; target_afterburner = 0.0f; target_thrust = 0; entity_thrust = 0; entity_controlflags = 0; } /* -- SNAPPY ------------------------------------------ */ current_target_afterburner = target_afterburner; if (current_target_afterburner < 0.0f) { target_thrust = 0; } entity_thrust = target_thrust; current_target_strafe = target_strafe; current_target_vstrafe = target_vstrafe; current_target_roll = target_roll; current_target_pitch = target_pitch; current_target_direction = target_direction; // clamp values math::clamp(entity_thrust, 0.0f, 1.0f); /* -- NOT SNAPPY -------------------------------------- */ /* // update afterburner control target if (current_target_afterburner < target_afterburner) { current_target_afterburner += afterburner_reaction * seconds; if (current_target_afterburner > target_afterburner) current_target_afterburner = target_afterburner; } else if (current_target_afterburner > target_afterburner) { current_target_afterburner -= afterburner_reaction * seconds; if (current_target_afterburner < target_afterburner) current_target_afterburner = target_afterburner; } // update thrust control target if (current_target_afterburner < 0.0f) { target_thrust = 0; } if (entity_thrust < target_thrust) { entity_thrust += thrust_reaction * seconds; if (entity_thrust > target_thrust) entity_thrust = target_thrust; } else if (entity_thrust > target_thrust) { entity_thrust -= thrust_reaction * seconds; if (entity_thrust < target_thrust) entity_thrust = target_thrust; } math::clamp(entity_thrust, 0.0f, 1.0f); // update strafe control target if (current_target_strafe < target_strafe) { current_target_strafe += strafe_reaction * seconds; if (current_target_strafe > target_strafe) current_target_strafe = target_strafe; } else if (current_target_strafe > target_strafe) { current_target_strafe -= strafe_reaction * seconds; if (current_target_strafe < target_strafe) current_target_strafe = target_strafe; } // update vstrafe control target if (current_target_vstrafe < target_vstrafe) { current_target_vstrafe += strafe_reaction * seconds; if (current_target_vstrafe > target_vstrafe) current_target_vstrafe = target_vstrafe; } else if (current_target_vstrafe > target_vstrafe) { current_target_vstrafe -= strafe_reaction * seconds; if (current_target_vstrafe < target_vstrafe) current_target_vstrafe = target_vstrafe; } // update direction control target if (current_target_direction < target_direction) { current_target_direction += direction_reaction * seconds; if (current_target_direction > target_direction) { current_target_direction = target_direction; } } else if (current_target_direction > target_direction) { current_target_direction -= direction_reaction * seconds; if (current_target_direction < target_direction) { current_target_direction = target_direction; } } // update pitch control target if (current_target_pitch < target_pitch) { current_target_pitch += direction_reaction * seconds; if (current_target_pitch > target_pitch) current_target_pitch = target_pitch; } else if (current_target_pitch > target_pitch) { current_target_pitch -= direction_reaction * seconds; if (current_target_pitch < target_pitch) current_target_pitch = target_pitch; } // update roll control target if (current_target_roll < target_roll) { current_target_roll += direction_reaction * seconds; if (current_target_roll > target_roll) current_target_roll = target_roll; } else if (current_target_roll > target_roll) { current_target_roll -= direction_reaction * seconds; if (current_target_roll < target_roll) current_target_roll = target_roll; } */ EntityControlable::frame(elapsed); } } // namespace game