diff options
Diffstat (limited to 'src/game/base/ship.cc')
-rw-r--r-- | src/game/base/ship.cc | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/src/game/base/ship.cc b/src/game/base/ship.cc new file mode 100644 index 0000000..0fdfd3f --- /dev/null +++ b/src/game/base/ship.cc @@ -0,0 +1,464 @@ +/* + 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 <cmath> +#include <iostream> + +#include "auxiliary/functions.h" +#include "core/gameserver.h" +#include "core/entity.h" +#include "base/base.h" +#include "base/ship.h" +#include "math/functions.h" + + + +using math::degrees360f; +using math::degrees180f; + +namespace base { + +const float MIN_DELTA = 0.000001f; + +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; + + entity_color = owner->color(); + entity_color_second = owner->color_second(); + + ship_shipmodel = shipmodel; + ship_jumpdrive = shipmodel->shipmodel_jumpdrive; + + ship_impulsedrive_timer = 0; + ship_jumpdrive_timer = 0; + + reset(); +} + +Ship::~Ship() +{ + +} + +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_afterburner = 0.0f; +} +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; + entity_timer = 0; + } + + entity_eventstate = core::Entity::ImpulseInitiate; + if (Base::instance()->g_devel->value()) { + entity_timer = 0; + } else { + entity_timer = 3; + } + ship_impulsedrive_timer = core::server()->time(); + entity_dirty = true; + } + + entity_dirty = true; +} + +void Ship::jump(std::string const &args) +{ + // devel mode provides instant jump access to arbitrary systems + if (Base::instance()->g_devel->value() && (args.size())) { + + core::Zone *jumptargetzone = 0; + 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); + jumptargetzone = core::Zone::find_zone(target); + if (!jumptargetzone) { + core::server()->send(owner(), "Unknown system '" + target + '\''); + return; + } + + if (jumptargetzone == zone()) { + core::server()->send(owner(), "Already in the " + jumptargetzone->name() + '.'); + return; + } + + core::server()->send(owner(), "Jumping to the " + jumptargetzone->name()); + set_zone(jumptargetzone); + if (owner()->control() == (EntityControlable*) this) + owner()->set_zone(jumptargetzone); + + ship_jumpdrive_timer = 0; + entity_timer = 0; + entity_eventstate = core::Entity::Jump; + entity_dirty = true; + return; + + } else { + if (!jumpdrive() && !Base::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_jumpdrive_timer = 0; + entity_timer = 0; + entity_eventstate = core::Entity::Normal; + return; + } + + if (!find_closest_jumppoint()) { + return; + } + + entity_eventstate = core::Entity::JumpInitiate; + if (Base::instance()->g_devel->value()) { + entity_timer = 0; + } else { + entity_timer = 8; + } + + ship_jumpdrive_timer = core::server()->time(); + entity_dirty = true; + } +} + +JumpPoint * Ship::find_closest_jumppoint() +{ + // find closest 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) { + JumpPoint *te = static_cast<JumpPoint *>(entity); + float d1 = math::distance(location(), te->location()); + if ((d < 0) || (d1 < d)) { + d = d1; + jumppoint = te; + } + } + } + + if (jumppoint && jumppoint->target()) { + if (Base::instance()->g_jumppointrange->value() < d) { + core::server()->send(owner(), "Jumppoint out of range!"); + return 0; + } else { + core::server()->send(owner(), "Jumping to the " + jumppoint->target()->zone()->name()); + return jumppoint; + } + } else { + core::server()->send(owner(), "No jumppoints found!"); + return 0; + } + + return 0; +} + +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(); + + entity_movement = 0; + + // 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) { + JumpPoint *jumppoint = find_closest_jumppoint(); + + if (jumppoint) { + set_zone(jumppoint->target()->zone()); + if (owner()->control() == (EntityControlable*) this) + owner()->set_zone(jumppoint->target()->zone()); + entity_eventstate = core::Entity::Jump; + entity_location.assign(jumppoint->target()->location() + location() - jumppoint->location()); + } else { + entity_eventstate = core::Entity::Normal; + } + ship_jumpdrive_timer = 0; + entity_timer = 0; + + 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; + target_afterburner = 0.0f; + target_thrust = 0; + + } else if (entity_eventstate == core::Entity::Jump) { + // control is disabled while the jumpdrive is activated + target_thrust = 0; + target_pitch = 0; + target_roll = 0; + target_direction = 0; + target_afterburner = 0.0f; + target_thrust = 0; + + // 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 = Base::instance()->g_impulsespeed->value(); + actual_acceleration = Base::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 = 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); + math::clamp(target_afterburner, -1.0f, 1.0f); + actual_turnspeed *= 0.5; + + } else if (entity_eventstate == core::Entity::Impulse) { + + // clamp input values + target_thrust = 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); + target_afterburner = 0.0f; + actual_maxspeed = Base::instance()->g_impulsespeed->value(); + actual_acceleration = Base::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); + math::clamp(target_afterburner, -1.0f, 1.0f); + + if (speed() > actual_maxspeed * 1.15f) { + actual_acceleration = Base::instance()->g_impulseacceleration->value(); + actual_turnspeed *= 0.5; + } + + } + + // 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 afterburner + if (current_target_afterburner < target_afterburner) { + current_target_afterburner += 2.0f * seconds; + if (current_target_afterburner > target_afterburner) + current_target_afterburner = target_afterburner; + } else if (current_target_afterburner > target_afterburner) { + current_target_afterburner -= 2.0f * seconds; + if (current_target_afterburner < target_afterburner) + current_target_afterburner = target_afterburner; + } + + // update thrust + if (current_target_afterburner < 0.0f) { + target_thrust = 0; + } + + 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); + float actual_thrust = entity_thrust + current_target_afterburner * 0.15f; + + if ((entity_eventstate == core::Entity::ImpulseInitiate) || (entity_eventstate == core::Entity::Impulse)) { + actual_thrust = 1.0f; + } + + // update speed + if (entity_speed < actual_thrust * actual_maxspeed) { + entity_speed += actual_acceleration * seconds; + if (entity_speed > actual_thrust * actual_maxspeed) { + entity_speed = actual_thrust * actual_maxspeed; + } + } else if(entity_speed > actual_thrust * actual_maxspeed) { + entity_speed -= actual_acceleration * seconds; + if (entity_speed < actual_thrust * actual_maxspeed) { + entity_speed = actual_thrust * actual_maxspeed; + } + } + + // update strafe + if (current_target_strafe < target_strafe) { + current_target_strafe += 2.0f * seconds; + if (current_target_strafe > target_strafe) + current_target_strafe = target_strafe; + } else if (current_target_strafe > target_strafe) { + current_target_strafe -= 2.0f * seconds; + if (current_target_strafe < target_strafe) + current_target_strafe = target_strafe; + } + + if (fabs(current_target_strafe) > MIN_DELTA) { + entity_location += entity_axis.left() * (current_target_strafe * Base::instance()->g_strafespeed->value()); + } + + entity_movement = target_thrust; + entity_movement = math::max(entity_movement, fabs(current_target_pitch)); + entity_movement = math::max(entity_movement, fabs(current_target_direction)); + entity_movement = math::max(entity_movement, fabs(current_target_roll)); + entity_movement = math::max(entity_movement, fabs(current_target_afterburner)); + entity_movement = math::max(entity_movement, fabs(current_target_strafe)); + + if (entity_speed) { + entity_location += entity_axis.forward() * entity_speed * seconds; + entity_dirty = true; + } else if (entity_movement > 0.0f) { + entity_dirty = true; + } +} + +} // namespace game |