// Get the amount of time it would take the given weapon to reach the given // target, assuming it can be fired in any direction (i.e. turreted). For // non-turreted weapons this can be used to calculate the ideal direction to // point the ship in. double Armament::RendezvousTime(const Point &p, const Point &v, double vp) { // How many steps will it take this projectile // to intersect the target? // (p.x + v.x*t)^2 + (p.y + v.y*t)^2 = vp^2*t^2 // p.x^2 + 2*p.x*v.x*t + v.x^2*t^2 // + p.y^2 + 2*p.y*v.y*t + v.y^2t^2 // - vp^2*t^2 = 0 // (v.x^2 + v.y^2 - vp^2) * t^2 // + (2 * (p.x * v.x + p.y * v.y)) * t // + (p.x^2 + p.y^2) = 0 double a = v.Dot(v) - vp * vp; double b = 2. * p.Dot(v); double c = p.Dot(p); double discriminant = b * b - 4 * a * c; if(discriminant < 0.) return numeric_limits<double>::quiet_NaN(); discriminant = sqrt(discriminant); // The solutions are b +- discriminant. // But it's not a solution if it's negative. double r1 = (-b + discriminant) / (2. * a); double r2 = (-b - discriminant) / (2. * a); if(r1 >= 0. && r2 >= 0.) return min(r1, r2); else if(r1 >= 0. || r2 >= 0.) return max(r1, r2); return numeric_limits<double>::quiet_NaN(); }
// GetSecondAngleAxis looks for the axis defined by the pointer finger // And the left base bool GloveType::GetBothAxes(int i) { if (!GetFirstAxis(i)) return false; if (axis_points2_.size() == 2) { // Check the first Axis validity if (base_left_->current_ == 0 || base_right_->current_ == 0) { return false; } /*check second axis*/ Point u; if (fore_->current_ == 0) { return false; } else { u = fore_->Sub(*base_left_); } Point v = fore_->Sub(*base_left_); axis2_ = u.Sub(v.Normalize().Times(u.Dot(v))).Normalize(); // Now find the normal component return true; } else { if (base_left_->current_ == 0 || fore_->current_ == 0) return false; axis_points2_.push_back(fore_); axis_points2_.push_back(base_left_); Point v = fore_->Sub(*base_left_); axis2_ = v.Sub(axis1_.Times(v.Dot(axis1_))).Normalize(); Point y_axis; y_axis.Init(0, 1, 0); original_axis2_ = y_axis; return true; } }
//---------------------------------------------------------------------------- Plane::Plane (const Vector& normal, const Point& p) { mTuple[0] = normal[0]; mTuple[1] = normal[1]; mTuple[2] = normal[2]; mTuple[3] = -p.Dot(normal); }
//---------------------------------------------------------------------------- Plane::Plane (const Point& p0, const Point& p1, const Point& p2) { Vector edge1 = p1 - p0; Vector edge2 = p2 - p0; Vector normal = edge1.UnitCross(edge2); mTuple[0] = normal[0]; mTuple[1] = normal[1]; mTuple[2] = normal[2]; mTuple[3] = -p0.Dot(normal); }
double AI::TurnToward(const Ship &ship, const Point &vector) { static const double RAD_TO_DEG = 180. / 3.14159265358979; Point facing = ship.Facing().Unit(); double cross = vector.Cross(facing); if(vector.Dot(facing) > 0.) { double angle = asin(cross / vector.Length()) * RAD_TO_DEG; if(fabs(angle) <= ship.TurnRate()) return -angle / ship.TurnRate(); } bool left = cross < 0.; return left - !left; }
DrawList::Item::Item(const Animation &animation, Point pos, Point unit, Point blur, float clip, int step) : position{static_cast<float>(pos.X()), static_cast<float>(pos.Y())}, clip(clip), flags(animation.GetSwizzle()) { Animation::Frame frame = animation.Get(step); tex0 = frame.first; tex1 = frame.second; flags |= static_cast<uint32_t>(frame.fade * 256.f) << 8; double width = animation.Width(); double height = animation.Height(); Point uw = unit * width; Point uh = unit * height; if(clip < 1.) { // "clip" is the fraction of its height that we're clipping the sprite // to. We still want it to start at the same spot, though. pos -= uh * ((1. - clip) * .5); position[0] = static_cast<float>(pos.X()); position[1] = static_cast<float>(pos.Y()); uh *= clip; } // (0, -1) means a zero-degree rotation (since negative Y is up). transform[0] = -uw.Y(); transform[1] = uw.X(); transform[2] = -uh.X(); transform[3] = -uh.Y(); // Calculate the blur vector, in texture coordinates. This should be done by // projecting the blur vector onto the unit vector and then scaling it based // on the sprite size. But, the unit vector first has to be normalized (i.e. // divided by the unit vector length), and the sprite size also has to be // multiplied by the unit vector size, so: double zoomCorrection = 4. * unit.LengthSquared(); this->blur[0] = unit.Cross(blur) / (width * zoomCorrection); this->blur[1] = -unit.Dot(blur) / (height * zoomCorrection); }
DrawList::Item::Item(const Body &body, Point pos, Point blur, float cloak, float clip, int swizzle, int step) : position{static_cast<float>(pos.X()), static_cast<float>(pos.Y())}, clip(clip), flags(swizzle) { Body::Frame frame = body.GetFrame(step); tex0 = frame.first; tex1 = frame.second; flags |= static_cast<uint32_t>(frame.fade * 256.f) << 8; double width = body.Width(); double height = body.Height(); Point unit = body.Facing().Unit(); Point uw = unit * width; Point uh = unit * height; if(clip < 1.) { // "clip" is the fraction of its height that we're clipping the sprite // to. We still want it to start at the same spot, though. pos -= uh * ((1. - clip) * .5); position[0] = static_cast<float>(pos.X()); position[1] = static_cast<float>(pos.Y()); uh *= clip; } // (0, -1) means a zero-degree rotation (since negative Y is up). transform[0] = -uw.Y(); transform[1] = uw.X(); transform[2] = -uh.X(); transform[3] = -uh.Y(); // Calculate the blur vector, in texture coordinates. this->blur[0] = unit.Cross(blur) / (width * 4.); this->blur[1] = -unit.Dot(blur) / (height * 4.); if(cloak > 0.) Cloak(cloak); }
// This returns false if it is time to delete this projectile. bool Projectile::Move(list<Effect> &effects) { if(--lifetime <= 0) { if(lifetime > -100) for(const auto &it : weapon->DieEffects()) for(int i = 0; i < it.second; ++i) { effects.push_back(*it.first); effects.back().Place(position, velocity, angle); } return false; } for(const auto &it : weapon->LiveEffects()) if(!Random::Int(it.second)) { effects.push_back(*it.first); effects.back().Place(position, velocity, angle); } // If the target has left the system, stop following it. Also stop if the // target has been captured by a different government. const Ship *target = cachedTarget; if(target) { target = targetShip.lock().get(); if(!target || !target->IsTargetable() || target->GetGovernment() != targetGovernment) { targetShip.reset(); cachedTarget = nullptr; target = nullptr; } } double turn = weapon->Turn(); double accel = weapon->Acceleration(); int homing = weapon->Homing(); if(target && homing && !Random::Int(60)) CheckLock(*target); if(target && homing && hasLock) { Point d = position - target->Position(); double drag = weapon->Drag(); double trueVelocity = drag ? accel / drag : velocity.Length(); double stepsToReach = d.Length() / trueVelocity; bool isFacingAway = d.Dot(angle.Unit()) > 0.; // At the highest homing level, compensate for target motion. if(homing >= 4) { // Adjust the target's position based on where it will be when we // reach it (assuming we're pointed right towards it). d -= stepsToReach * target->Velocity(); stepsToReach = d.Length() / trueVelocity; } double cross = d.Unit().Cross(angle.Unit()); // The very dumbest of homing missiles lose their target if pointed // away from it. if(isFacingAway && homing == 1) targetShip.reset(); else { double desiredTurn = TO_DEG * asin(cross); if(fabs(desiredTurn) > turn) turn = copysign(turn, desiredTurn); else turn = desiredTurn; // Levels 3 and 4 stop accelerating when facing away. if(homing >= 3) { double stepsToFace = desiredTurn / turn; // If you are facing away from the target, stop accelerating. if(stepsToFace * 1.5 > stepsToReach) accel = 0.; } } } // If a weapon is homing but has no target, do not turn it. else if(homing) turn = 0.; if(turn) angle += Angle(turn); if(accel) { velocity += accel * angle.Unit(); velocity *= 1. - weapon->Drag(); } position += velocity; if(target && (position - target->Position()).Length() < weapon->SplitRange() && !Random::Int(10)) lifetime = 0; return true; }
void dxTriMeshData::Preprocess() { #if dTRIMESH_ENABLED // If this mesh has already been preprocessed, exit if (UseFlags) return; udword numTris = Mesh.GetNbTriangles(); udword numEdges = numTris * 3; UseFlags = new uint8[numTris]; memset(UseFlags, 0, sizeof(uint8) * numTris); EdgeRecord* records = new EdgeRecord[numEdges]; // Make a list of every edge in the mesh const IndexedTriangle* tris = Mesh.GetTris(); const unsigned tristride = Mesh.GetTriStride(); for (unsigned int i = 0; i < numTris; i++) { SetupEdge(&records[i*3], 0, i, tris->mVRef); SetupEdge(&records[i*3+1], 1, i, tris->mVRef); SetupEdge(&records[i*3+2], 2, i, tris->mVRef); tris = (const IndexedTriangle*)(((uint8*)tris) + tristride); } // Sort the edges, so the ones sharing the same verts are beside each other qsort(records, numEdges, sizeof(EdgeRecord), EdgeCompare); // Go through the sorted list of edges and flag all the edges and vertices that we need to use for (unsigned int i = 0; i < numEdges; i++) { EdgeRecord* rec1 = &records[i]; EdgeRecord* rec2 = 0; if (i < numEdges - 1) rec2 = &records[i+1]; if (rec2 && rec1->VertIdx1 == rec2->VertIdx1 && rec1->VertIdx2 == rec2->VertIdx2) { VertexPointers vp; ConversionArea vc; Mesh.GetTriangle(vp, rec1->TriIdx, vc); // Get the normal of the first triangle Point triNorm = (*vp.Vertex[2] - *vp.Vertex[1]) ^ (*vp.Vertex[0] - *vp.Vertex[1]); triNorm.Normalize(); // Get the vert opposite this edge in the first triangle Point oppositeVert1 = GetOppositeVert(rec1, vp.Vertex); // Get the vert opposite this edge in the second triangle Mesh.GetTriangle(vp, rec2->TriIdx, vc); Point oppositeVert2 = GetOppositeVert(rec2, vp.Vertex); float dot = triNorm.Dot((oppositeVert2 - oppositeVert1).Normalize()); // We let the dot threshold for concavity get slightly negative to allow for rounding errors static const float kConcaveThresh = -0.000001f; // This is a concave edge, leave it for the next pass if (dot >= kConcaveThresh) rec1->Concave = true; // If this is a convex edge, mark its vertices and edge as used else UseFlags[rec1->TriIdx] |= rec1->Vert1Flags | rec1->Vert2Flags | rec1->EdgeFlags; // Skip the second edge i++; } // This is a boundary edge else { UseFlags[rec1->TriIdx] |= rec1->Vert1Flags | rec1->Vert2Flags | rec1->EdgeFlags; } } // Go through the list once more, and take any edge we marked as concave and // clear it's vertices flags in any triangles they're used in for (unsigned int i = 0; i < numEdges; i++) { EdgeRecord& er = records[i]; if (er.Concave) { for (unsigned int j = 0; j < numEdges; j++) { EdgeRecord& curER = records[j]; if (curER.VertIdx1 == er.VertIdx1 || curER.VertIdx1 == er.VertIdx2) UseFlags[curER.TriIdx] &= ~curER.Vert1Flags; if (curER.VertIdx2 == er.VertIdx1 || curER.VertIdx2 == er.VertIdx2) UseFlags[curER.TriIdx] &= ~curER.Vert2Flags; } } } delete [] records; #endif // dTRIMESH_ENABLED }
// VectorPerpendicularTo finds the vector perpendicular to a line // line: a vector of two points that define a line in R3 // return: the shortest distance from this point to that line Point Point::VectorPerpendicularTo(vector<Point> line) { Point v = line[1].Sub(line[0]); Point u = this->Sub(line[0]); return u.Sub(v.Normalize().Times(v.Dot(u))); }
// Only override the ones you need; the default action is to return false. bool MapDetailPanel::KeyDown(SDL_Keycode key, Uint16 mod, const Command &command) { if(command.Has(Command::MAP) || key == 'd' || key == SDLK_ESCAPE || (key == 'w' && (mod & (KMOD_CTRL | KMOD_GUI)))) GetUI()->Pop(this); else if(key == SDLK_PAGEUP || key == SDLK_PAGEDOWN || key == 'i') { GetUI()->Pop(this); GetUI()->Push(new MissionPanel(*this)); } else if(key == 'o') { GetUI()->Pop(this); GetUI()->Push(new MapOutfitterPanel(*this)); } else if(key == 's') { GetUI()->Pop(this); GetUI()->Push(new MapShipyardPanel(*this)); } else if((key == SDLK_TAB || command.Has(Command::JUMP)) && player.Flagship()) { // Toggle to the next link connected to the "source" system. If the // shift key is down, the source is the end of the travel plan; otherwise // it is one step before the end. vector<const System *> &plan = player.TravelPlan(); const System *source = plan.empty() ? player.GetSystem() : plan.front(); const System *next = nullptr; Point previousUnit = Point(0., -1.); if(!plan.empty() && !(mod & KMOD_SHIFT)) { previousUnit = plan.front()->Position(); plan.erase(plan.begin()); next = source; source = plan.empty() ? player.GetSystem() : plan.front(); previousUnit = (previousUnit - source->Position()).Unit(); } Point here = source->Position(); // Depending on whether the flagship has a jump drive, the possible links // we can travel along are different: bool hasJumpDrive = player.Flagship()->Attributes().Get("jump drive"); const vector<const System *> &links = hasJumpDrive ? source->Neighbors() : source->Links(); double bestAngle = 2. * PI; for(const System *it : links) { if(!player.HasSeen(it)) continue; if(!(hasJumpDrive || player.HasVisited(it) || player.HasVisited(source))) continue; Point unit = (it->Position() - here).Unit(); double angle = acos(unit.Dot(previousUnit)); if(unit.Cross(previousUnit) >= 0.) angle = 2. * PI - angle; if(angle <= bestAngle) { next = it; bestAngle = angle; } } if(next) { plan.insert(plan.begin(), next); Select(next); } } else if((key == SDLK_DELETE || key == SDLK_BACKSPACE) && player.HasTravelPlan()) { vector<const System *> &plan = player.TravelPlan(); plan.erase(plan.begin()); Select(plan.empty() ? player.GetSystem() : plan.front()); } else if(key == SDLK_DOWN) { if(commodity < 0 || commodity == 9) commodity = 0; else ++commodity; } else if(key == SDLK_UP) { if(commodity <= 0) commodity = 9; else --commodity; } else if(key == 'f') GetUI()->Push(new Dialog( this, &MapDetailPanel::DoFind, "Search for:")); else if(key == '+' || key == '=') ZoomMap(); else if(key == '-') UnzoomMap(); else return false; return true; }