// temporary one-point version static void CollideWithTerrain(Body *body) { if (!body->IsType(Object::DYNAMICBODY)) return; DynamicBody *dynBody = static_cast<DynamicBody*>(body); if (!dynBody->IsMoving()) return; Frame *f = body->GetFrame(); if (!f || !f->GetBody() || f != f->GetBody()->GetFrame()) return; if (!f->GetBody()->IsType(Object::TERRAINBODY)) return; TerrainBody *terrain = static_cast<TerrainBody*>(f->GetBody()); const Aabb &aabb = dynBody->GetAabb(); double altitude = body->GetPosition().Length() + aabb.min.y; if (altitude >= terrain->GetMaxFeatureRadius()) return; double terrHeight = terrain->GetTerrainHeight(body->GetPosition().Normalized()); if (altitude >= terrHeight) return; CollisionContact c; c.pos = body->GetPosition(); c.normal = c.pos.Normalized(); c.depth = terrHeight - altitude; c.userData1 = static_cast<void*>(body); c.userData2 = static_cast<void*>(f->GetBody()); hitCallback(&c); }
// ok, need thing to step down through bodies and find closest approach // modify targpos directly to aim short of dangerous bodies static bool ParentSafetyAdjust(Ship *ship, Frame *targframe, vector3d &targpos, vector3d &targvel) { Body *body = 0; Frame *frame = targframe->GetNonRotFrame(); while (frame) { if (ship->GetFrame()->GetNonRotFrame() == frame) break; // ship in frame, stop if (frame->GetBody()) body = frame->GetBody(); // ignore grav points? double sdist = ship->GetPositionRelTo(frame).Length(); if (sdist < frame->GetRadius()) break; // ship inside frame, stop frame = frame->GetParent()->GetNonRotFrame(); // check next frame down } if (!body) return false; // aim for zero velocity at surface of that body // still along path to target vector3d targpos2 = targpos - ship->GetPosition(); double targdist = targpos2.Length(); double bodydist = body->GetPositionRelTo(ship).Length() - MaxEffectRad(body, ship)*1.5; if (targdist < bodydist) return false; targpos -= (targdist - bodydist) * targpos2 / targdist; targvel = body->GetVelocityRelTo(ship->GetFrame()); return true; }
/* * Method: GetGroundPosition * * Get latitude, longitude and altitude of a dynamic body close to the ground or nil the body is not a dynamic body * or is not close to the ground. * * > latitude, longitude, altitude = body:GetGroundPosition() * * Returns: * * latitude - the latitude of the body in radians * longitude - the longitude of the body in radians * altitude - altitude above the ground in meters * * Examples: * * > -- Get ground position of the player * > local lat, long, alt = Game.player:GetGroundPosition() * > lat = math.rad2deg(lat) * > long = math.rad2deg(long) * * Availability: * * July 2013 * * Status: * * experimental */ static int l_body_get_ground_position(lua_State *l) { Body *b = LuaObject<Body>::CheckFromLua(1); if (!b->IsType(Object::DYNAMICBODY)) { lua_pushnil(l); return 1; } Frame *f = b->GetFrame(); if (!f->IsRotFrame()) return 0; vector3d pos = b->GetPosition(); double latitude = atan2(pos.y, sqrt(pos.x * pos.x + pos.z * pos.z)); double longitude = atan2(pos.x, pos.z); lua_pushnumber(l, latitude); lua_pushnumber(l, longitude); Body *astro = f->GetBody(); if (astro->IsType(Object::TERRAINBODY)) { double radius = static_cast<TerrainBody *>(astro)->GetTerrainHeight(pos.Normalized()); double altitude = pos.Length() - radius; lua_pushnumber(l, altitude); } else { lua_pushnil(l); } return 3; }
/* * Attribute: frameBody * * The non-dynamic body attached to the frame this dynamic body is in. * * Only valid for dynamic <Bodies>. For non-dynamic bodies <frameBody> will be * nil. * * <frameBody> can also be nil if this dynamic body is in a frame with no * non-dynamic body. This most commonly occurs when the player is in * hyperspace. * * Availability: * * alpha 12 * * Status: * * experimental */ static int l_body_attr_frame_body(lua_State *l) { Body *b = LuaObject<Body>::CheckFromLua(1); if (!b->IsType(Object::DYNAMICBODY)) { lua_pushnil(l); return 1; } Frame *f = b->GetFrame(); LuaObject<Body>::PushToLua(f->GetBody()); return 1; }
// Creates a child AI command to fly the ship to nearest transit location if it wasn't within transit location // Returns false if ship is already in transit location, true if it wasn't and a new AI command is created to go there bool AIParagonCmdFlyTo::GoToTransitDistance() { m_remainingDistance = m_data.ship_to_target_distance; // is target far enough for transit and transit is not possible right now? if (m_remainingDistance > VICINITY_DISTANCE && !m_ship->IsTransitPossible()) { Frame* sframe = m_ship->GetFrame(); Body* sbody = sframe->GetBody(); assert(sframe && sbody); double transit_radius = GetTransitRadius(sbody); // if transit is not possible (IsTransitPossible) then ship is within planet or spacestation so no need to check for that vector3d transit_position = (m_ship->GetPosition() - sbody->GetPosition()).Normalized() * transit_radius; m_child = new AIParagonCmdGoTo(m_ship, sframe, transit_position); return true; } else { return false; } }
static Frame *MakeFrameFor(SystemBody *sbody, Body *b, Frame *f) { if (!sbody->parent) { if (b) b->SetFrame(f); f->SetBodies(sbody, b); return f; } if (sbody->type == SystemBody::TYPE_GRAVPOINT) { Frame *orbFrame = new Frame(f, sbody->name.c_str()); orbFrame->SetBodies(sbody, b); orbFrame->SetRadius(sbody->GetMaxChildOrbitalDistance()*1.1); return orbFrame; } SystemBody::BodySuperType supertype = sbody->GetSuperType(); if ((supertype == SystemBody::SUPERTYPE_GAS_GIANT) || (supertype == SystemBody::SUPERTYPE_ROCKY_PLANET)) { // for planets we want an non-rotating frame for a few radii // and a rotating frame with no radius to contain attached objects double frameRadius = std::max(4.0*sbody->GetRadius(), sbody->GetMaxChildOrbitalDistance()*1.05); Frame *orbFrame = new Frame(f, sbody->name.c_str(), Frame::FLAG_HAS_ROT); orbFrame->SetBodies(sbody, b); orbFrame->SetRadius(frameRadius); //printf("\t\t\t%s has frame size %.0fkm, body radius %.0fkm\n", sbody->name.c_str(), // (frameRadius ? frameRadius : 10*sbody->GetRadius())*0.001f, // sbody->GetRadius()*0.001f); assert(sbody->rotationPeriod != 0); Frame *rotFrame = new Frame(orbFrame, sbody->name.c_str(), Frame::FLAG_ROTATING); rotFrame->SetBodies(sbody, b); // rotating frame has atmosphere radius or feature height, whichever is larger rotFrame->SetRadius(b->GetPhysRadius()); matrix3x3d rotMatrix = matrix3x3d::RotateX(sbody->axialTilt.ToDouble()); double angSpeed = 2.0*M_PI/sbody->GetRotationPeriod(); rotFrame->SetAngSpeed(angSpeed); if (sbody->rotationalPhaseAtStart != fixed(0)) rotMatrix = rotMatrix * matrix3x3d::RotateY(sbody->rotationalPhaseAtStart.ToDouble()); rotFrame->SetOrient(rotMatrix); b->SetFrame(rotFrame); return orbFrame; } else if (supertype == SystemBody::SUPERTYPE_STAR) { // stars want a single small non-rotating frame // bigger than it's furtherest orbiting body. // if there are no orbiting bodies use a frame of several radii. Frame *orbFrame = new Frame(f, sbody->name.c_str()); orbFrame->SetBodies(sbody, b); orbFrame->SetRadius(std::max(10.0*sbody->GetRadius(), sbody->GetMaxChildOrbitalDistance()*1.1)); b->SetFrame(orbFrame); return orbFrame; } else if (sbody->type == SystemBody::TYPE_STARPORT_ORBITAL) { // space stations want non-rotating frame to some distance // and a zero-size rotating frame Frame *orbFrame = new Frame(f, sbody->name.c_str(), Frame::FLAG_HAS_ROT); orbFrame->SetBodies(sbody, b); // orbFrame->SetRadius(10*sbody->GetRadius()); orbFrame->SetRadius(20000.0); // 4x standard parking radius b->SetFrame(orbFrame); return orbFrame; // assert(sbody->rotationPeriod != 0); // rotFrame = new Frame(orbFrame, sbody->name.c_str(), Frame::FLAG_ROTATING); // rotFrame->SetBodies(sbody, b); // rotFrame->SetRadius(0.0); // rotFrame->SetAngVelocity(vector3d(0.0,double(static_cast<SpaceStation*>(b)->GetDesiredAngVel()),0.0)); // b->SetFrame(rotFrame); } else if (sbody->type == SystemBody::TYPE_STARPORT_SURFACE) { // just put body into rotating frame of planet, not in its own frame // (because collisions only happen between objects in same frame, // and we want collisions on starport and on planet itself) Frame *rotFrame = f->GetRotFrame(); b->SetFrame(rotFrame); assert(rotFrame->IsRotFrame()); assert(rotFrame->GetBody()->IsType(Object::PLANET)); matrix3x3d rot; vector3d pos; Planet *planet = static_cast<Planet*>(rotFrame->GetBody()); RelocateStarportIfUnderwaterOrBuried(sbody, rotFrame, planet, pos, rot); sbody->orbit.rotMatrix = rot; b->SetPosition(pos * planet->GetTerrainHeight(pos)); b->SetOrient(rot); return rotFrame; } else { assert(0); } return NULL; }
static Frame *MakeFrameFor(double at_time, SystemBody *sbody, Body *b, Frame *f) { if (!sbody->GetParent()) { if (b) b->SetFrame(f); f->SetBodies(sbody, b); return f; } if (sbody->GetType() == SystemBody::TYPE_GRAVPOINT) { Frame *orbFrame = new Frame(f, sbody->GetName().c_str()); orbFrame->SetBodies(sbody, b); orbFrame->SetRadius(sbody->GetMaxChildOrbitalDistance()*1.1); return orbFrame; } SystemBody::BodySuperType supertype = sbody->GetSuperType(); if ((supertype == SystemBody::SUPERTYPE_GAS_GIANT) || (supertype == SystemBody::SUPERTYPE_ROCKY_PLANET)) { // for planets we want an non-rotating frame for a few radii // and a rotating frame with no radius to contain attached objects double frameRadius = std::max(4.0*sbody->GetRadius(), sbody->GetMaxChildOrbitalDistance()*1.05); Frame *orbFrame = new Frame(f, sbody->GetName().c_str(), Frame::FLAG_HAS_ROT); orbFrame->SetBodies(sbody, b); orbFrame->SetRadius(frameRadius); //Output("\t\t\t%s has frame size %.0fkm, body radius %.0fkm\n", sbody->name.c_str(), // (frameRadius ? frameRadius : 10*sbody->GetRadius())*0.001f, // sbody->GetRadius()*0.001f); assert(sbody->IsRotating() != 0); Frame *rotFrame = new Frame(orbFrame, sbody->GetName().c_str(), Frame::FLAG_ROTATING); rotFrame->SetBodies(sbody, b); // rotating frame has atmosphere radius or feature height, whichever is larger rotFrame->SetRadius(b->GetPhysRadius()); matrix3x3d rotMatrix = matrix3x3d::RotateX(sbody->GetAxialTilt()); double angSpeed = 2.0*M_PI/sbody->GetRotationPeriod(); rotFrame->SetAngSpeed(angSpeed); if (sbody->HasRotationPhase()) rotMatrix = rotMatrix * matrix3x3d::RotateY(sbody->GetRotationPhaseAtStart()); rotFrame->SetInitialOrient(rotMatrix, at_time); b->SetFrame(rotFrame); return orbFrame; } else if (supertype == SystemBody::SUPERTYPE_STAR) { // stars want a single small non-rotating frame // bigger than it's furtherest orbiting body. // if there are no orbiting bodies use a frame of several radii. Frame *orbFrame = new Frame(f, sbody->GetName().c_str()); orbFrame->SetBodies(sbody, b); double frameRadius = std::max(10.0*sbody->GetRadius(), sbody->GetMaxChildOrbitalDistance()*1.1); // Respect the frame of other stars in the multi-star system. We still make sure that the frame ends outside // the body. For a minimum separation of 1.236 radii, nothing will overlap (see StarSystem::StarSystem()). if (sbody->GetParent() && frameRadius > AU * 0.11 * sbody->GetOrbMin()) frameRadius = std::max(1.1*sbody->GetRadius(), AU * 0.11 * sbody->GetOrbMin()); orbFrame->SetRadius(frameRadius); b->SetFrame(orbFrame); return orbFrame; } else if (sbody->GetType() == SystemBody::TYPE_STARPORT_ORBITAL) { // space stations want non-rotating frame to some distance Frame *orbFrame = new Frame(f, sbody->GetName().c_str()); orbFrame->SetBodies(sbody, b); // orbFrame->SetRadius(10*sbody->GetRadius()); orbFrame->SetRadius(20000.0); // 4x standard parking radius b->SetFrame(orbFrame); return orbFrame; } else if (sbody->GetType() == SystemBody::TYPE_STARPORT_SURFACE) { // just put body into rotating frame of planet, not in its own frame // (because collisions only happen between objects in same frame, // and we want collisions on starport and on planet itself) Frame *rotFrame = f->GetRotFrame(); b->SetFrame(rotFrame); assert(rotFrame->IsRotFrame()); assert(rotFrame->GetBody()->IsType(Object::PLANET)); matrix3x3d rot; vector3d pos; Planet *planet = static_cast<Planet*>(rotFrame->GetBody()); RelocateStarportIfUnderwaterOrBuried(sbody, rotFrame, planet, pos, rot); sbody->SetOrbitPlane(rot); b->SetPosition(pos * planet->GetTerrainHeight(pos)); b->SetOrient(rot); return rotFrame; } else { assert(0); } return 0; }
/* * Function: SpawnShipLandedNear * * Create a ship and place it on the surface near the given <Body>. * * > ship = Space.SpawnShipLandedNear(type, body, min, max) * * Parameters: * * type - the name of the ship * * body - the <Body> near which the ship should be spawned. It must be on the ground or close to it, * i.e. it must be in the rotating frame of the planetary body. * * min - minimum distance from the surface point below the body to place the ship, in Km * * max - maximum distance to place the ship * * Return: * * ship - a <Ship> object for the new ship * * Example: * * > -- spawn a ship 10km from the player * > local ship = Ship.SpawnShipLandedNear("viper_police", Game.player, 10, 10) * * Availability: * * July 2013 * * Status: * * experimental */ static int l_space_spawn_ship_landed_near(lua_State *l) { if (!Pi::game) luaL_error(l, "Game is not started"); LUA_DEBUG_START(l); const char *type = luaL_checkstring(l, 1); if (! ShipType::Get(type)) luaL_error(l, "Unknown ship type '%s'", type); Body *nearbody = LuaObject<Body>::CheckFromLua(2); const float min_dist = luaL_checknumber(l, 3); const float max_dist = luaL_checknumber(l, 4); if (min_dist > max_dist) luaL_error(l, "min_dist must not be larger than max_dist"); Ship *ship = new Ship(type); assert(ship); // XXX protect against spawning inside the body Frame * newframe = nearbody->GetFrame()->GetRotFrame(); if (!newframe->IsRotFrame()) luaL_error(l, "Body must be in rotating frame"); SystemBody *sbody = newframe->GetSystemBody(); if (sbody->GetSuperType() != SystemBody::SUPERTYPE_ROCKY_PLANET) luaL_error(l, "Body is not on a rocky planet"); if (max_dist > sbody->GetRadius()) luaL_error(l, "max_dist too large for planet radius"); // We assume that max_dist is much smaller than the planet radius, i.e. that our area is reasonably flat // So, we const vector3d up = nearbody->GetPosition().Normalized(); vector3d x; vector3d y; // Calculate a orthonormal basis for a horizontal plane. For numerical reasons we do that determining the smallest // coordinate and take the cross product with (1,0,0), (0,1,0) or (0,0,1) respectively to calculate the first vector. // The second vector is just the cross product of the up-vector and out first vector. if (up.x <= up.y && up.x <= up.z) { x = vector3d(0.0, up.z, -up.y).Normalized(); y = vector3d(-up.y*up.y - up.z*up.z, up.x*up.y, up.x*up.z).Normalized(); } else if (up.y <= up.x && up.y <= up.z) { x = vector3d(-up.z, 0.0, up.x).Normalized(); y = vector3d(up.x*up.y, -up.x*up.x - up.z*up.z, up.y*up.z).Normalized(); } else { x = vector3d(up.y, -up.x, 0.0).Normalized(); y = vector3d(up.x*up.z, up.y*up.z, -up.x*up.x - up.y*up.y).Normalized(); } Planet *planet = static_cast<Planet*>(newframe->GetBody()); const double radius = planet->GetSystemBody()->GetRadius(); const vector3d planar = MathUtil::RandomPointInCircle(min_dist * 1000.0, max_dist * 1000.0); vector3d pos = (radius * up + x * planar.x + y * planar.y).Normalized(); float latitude = atan2(pos.y, sqrt(pos.x*pos.x + pos.z * pos.z)); float longitude = atan2(pos.x, pos.z); Pi::game->GetSpace()->AddBody(ship); ship->SetLandedOn(planet, latitude, longitude); LuaObject<Ship>::PushToLua(ship); LUA_DEBUG_END(l, 1); return 1; }