Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Ability to limit turret firing arc #7094

Merged
merged 97 commits into from
May 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
3ec8e09
Introduce a capability to limit the rotation of a turret.
oo13 Oct 18, 2020
b4bf606
Refactoring: some names are changed.
oo13 Oct 21, 2020
653cba8
Refactoring: preserve the angles parameter of the constructor to reca…
oo13 Oct 22, 2020
d854fd8
The weapon has the "angle of traverse" attribute now.
oo13 Oct 23, 2020
480418f
A parameter "angle of traverse" affects an anti-missile turret now.
oo13 Oct 25, 2020
21779e3
Apply some feedbacks.
oo13 Oct 25, 2020
097c174
Fix a bug where causes a stuck turret when combining with an unrestri…
oo13 Nov 15, 2020
e132fbb
Fix a bug where a restricted turret might not return to the idle posi…
oo13 Nov 15, 2020
743895c
Change the parameter name "angle of traverse" to "turn range", and fi…
oo13 Dec 27, 2020
2869ea0
Change the attribute name "turn range" to "swept angle".
oo13 Jan 2, 2021
5424d08
Merge pull request #1 from endless-sky/master
1010todd Jan 12, 2021
5908eb1
Apply suggestions from code review
oo13 Jan 19, 2021
96e741b
Apply a suggestion by tehhowch.
oo13 Jan 20, 2021
8c8d4f6
Refactor: Add Angle::IsInRange(pair<Angle, Angle>).
oo13 Jan 20, 2021
6c9584d
Refactor: Rewrite some comments.
oo13 Jan 20, 2021
1cd3460
Refactor: Rename the struct AnglesParameter to BaseAttributes.
oo13 Jan 20, 2021
8b5fd8c
Avoid incorrect warning message.
oo13 Jan 20, 2021
1d069ed
The idle position of the turrets is calculated when installing, inste…
oo13 Jan 20, 2021
c8f57e3
Fix a wrong message of warning.
oo13 Jan 20, 2021
0e4dff3
Change the attribute name "swept angle" to "arc".
oo13 Jan 22, 2021
d1b63d5
Change the tip's entry "swept angle:" to "arc:".
oo13 Jul 21, 2021
8811090
Merge branch 'master' into limited_turret_turning_angle
oo13 Jul 21, 2021
22517dd
Merge branch 'endless-sky:master' into master
1010todd Apr 11, 2022
481422d
Merge branch 'endless-sky:master' into master
1010todd May 23, 2022
47dac5b
Merge branch 'endless-sky:master' into master
1010todd May 23, 2022
80f00a6
Merge branch 'endless-sky:master' into master
1010todd Jul 5, 2022
09ed7e6
Merge branch 'endless-sky:master' into master
1010todd Aug 3, 2022
d78439a
Merge branch 'limited_turret_turning_angle' of https://github.com/oo1…
1010todd Aug 4, 2022
d364cf1
Merge branch 'oo13-limited_turret_turning_angle' into limited-turret-arc
1010todd Aug 4, 2022
0a7fae5
Update
1010todd Aug 4, 2022
6750720
Make arc relative to turret angle.
1010todd Aug 4, 2022
8a552c5
Write warning.
1010todd Aug 4, 2022
57ca304
Make idle sweeping works with arc.
1010todd Aug 4, 2022
d39308f
Fix trailing whitespace.
1010todd Aug 4, 2022
5f1072c
Update source/AI.cpp
1010todd Aug 5, 2022
cf70a3d
Change variable name from arc to maxAngle for weapon.
1010todd Aug 5, 2022
b9b8369
Merge branch 'limited-turret-arc' of https://github.com/1010todd/endl…
1010todd Aug 5, 2022
4269a96
Corrected warning location
1010todd Aug 5, 2022
4ecc644
Remove (probably) unnecessary calculation.
1010todd Aug 5, 2022
cbcef29
Merge branch 'endless-sky:master' into master
1010todd Sep 2, 2022
90745ca
Corrected arc output to save file.
1010todd Sep 2, 2022
756b625
Apply comment grammar from code review
1010todd Sep 2, 2022
45d2fb5
arc pair to minArc and maxArc
1010todd Sep 2, 2022
491d52f
Move guard if to the top of UpdateArc()
1010todd Sep 2, 2022
5f5a083
Fix warning.
1010todd Sep 2, 2022
da0eb72
Merge branch 'endless-sky:master' into master
1010todd Oct 5, 2022
e0de25c
Apply suggestion from oo13
1010todd Oct 20, 2022
25c7eef
Don't round when writing to save file.
1010todd Oct 21, 2022
810d89c
Merge branch 'endless-sky:master' into master
1010todd Nov 21, 2022
ed03ee5
Merge branch 'endless-sky:master' into master
1010todd Nov 29, 2022
c153695
Merge branch 'master' into limited-turret-arc
1010todd Nov 29, 2022
660389c
Fix compile error.
1010todd Nov 29, 2022
d8e255c
Merge branch 'endless-sky:master' into master
1010todd Dec 4, 2022
b1b7956
Merge branch 'endless-sky:master' into limited-turret-arc
1010todd Dec 30, 2022
9c35d02
Codestyle fixes
1010todd Dec 30, 2022
a697d79
Merge branch 'endless-sky:master' into master
1010todd Jan 12, 2023
fee3e16
Merge branch 'endless-sky:master' into master
1010todd Jan 16, 2023
52db9cc
Merge branch 'endless-sky:master' into master
1010todd Jan 25, 2023
19523f1
Merge branch 'endless-sky:master' into master
1010todd Feb 2, 2023
d989281
Merge branch 'endless-sky:master' into master
1010todd Feb 21, 2023
381489e
Remove unnecessary include(?)
1010todd Feb 28, 2023
5f7fd7e
Add comment
1010todd Mar 1, 2023
66ecd02
Remove unnecessary IsInRange overload
1010todd Mar 1, 2023
93f11a7
Merge branch 'endless-sky:master' into master
1010todd Mar 20, 2023
d3f9b63
Merge branch 'endless-sky:master' into master
1010todd Apr 20, 2023
e2d769c
Merge branch 'endless-sky:master' into master
1010todd Apr 24, 2023
ee8a4b6
Apply suggestion
1010todd Apr 27, 2023
9f921e2
Merge remote-tracking branch 'origin' into limited-turret-arc
1010todd Apr 27, 2023
063502c
Merge branch 'limited-turret-arc' of https://github.com/1010todd/endl…
1010todd Apr 27, 2023
642bf63
Merge remote-tracking branch 'upstream' into limited-turret-arc
1010todd May 5, 2023
7634191
Fix angle flipping when saving again
1010todd May 5, 2023
48eaf09
Merge branch 'master' into limited-turret-arc
1010todd May 30, 2023
87fed4f
Merge branch 'endless-sky:master' into limited-turret-arc
1010todd Jul 6, 2023
d257a67
Merge branch 'master' into limited-turret-arc
1010todd Jul 28, 2023
715b885
Merge branch 'master' into limited-turret-arc
1010todd Aug 4, 2023
6229762
Merge branch 'endless-sky:master' into limited-turret-arc
1010todd Oct 5, 2023
25b62aa
Merge branch 'master' into limited-turret-arc
1010todd Oct 21, 2023
04789db
Merge branch 'master' into limited-turret-arc
1010todd Dec 19, 2023
a167f62
Merge branch 'master' into limited-turret-arc
1010todd Feb 13, 2024
d111417
Move arc check to FireSpecialSystem()
1010todd Feb 13, 2024
8b87f7d
More newlines
1010todd Feb 18, 2024
0393e23
Merge branch 'master' into limited-turret-arc
1010todd Feb 18, 2024
93f8276
Merge branch 'master' into limited-turret-arc
1010todd Mar 24, 2024
6b37ec5
No longer use number in var name
1010todd Mar 24, 2024
4e53cb7
Merge branch 'limited-turret-arc' of https://github.com/1010todd/endl…
1010todd Mar 24, 2024
285c83f
Apply suggestions from code review
1010todd Apr 22, 2024
608c8b7
Fix missing variable and duplicate declaration
1010todd Apr 22, 2024
4ba0a79
Apply suggestions from code review
1010todd Apr 29, 2024
9407bed
Remove #include <utility>
1010todd Apr 29, 2024
5137733
Remove another #include <utility>
1010todd May 3, 2024
be42176
Create test_angleIsInRange.cpp
1010todd May 3, 2024
36e05bc
Update CMakeLists.txt
1010todd May 3, 2024
8a9ee3b
Moved the test to test_angle.cpp
1010todd May 7, 2024
213c28b
Merge branch 'master' into limited-turret-arc
1010todd May 7, 2024
5a6fdbc
Add comment and remove unnecessary curly bracket
1010todd May 7, 2024
09eb2ba
Use swap line end for test stuffs back to LF.
1010todd May 7, 2024
5117608
Remove unnecessary variable.
petervdmeer May 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions data/_ui/tooltips.txt
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,9 @@ tip "shots / second:"
tip "turret turn rate:"
`Degrees this turret can turn per second.`

tip "arc:"
`The angular range of protection provided by this weapon, in degrees.`

tip "homing:"
`This projectile's ability to track its target. Weapons with better homing can adjust to the target's speed to plot a more effective intercept course.`

Expand Down
37 changes: 33 additions & 4 deletions source/AI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3356,7 +3356,7 @@ void AI::AimTurrets(const Ship &ship, FireCommand &command, bool opportunistic)
{
// Get the index of this weapon.
int index = &hardpoint - &ship.Weapons().front();
double offset = (hardpoint.HarmonizedAngle() - hardpoint.GetAngle()).Degrees();
double offset = (hardpoint.GetIdleAngle() - hardpoint.GetAngle()).Degrees();
command.SetAim(index, offset / hardpoint.GetOutfit()->TurretTurn());
}
return;
Expand All @@ -3374,8 +3374,12 @@ void AI::AimTurrets(const Ship &ship, FireCommand &command, bool opportunistic)
if(!previous && (Random::Int(60)))
continue;

Angle centerAngle = Angle(hardpoint.GetPoint());
double bias = (centerAngle - hardpoint.GetAngle()).Degrees() / 180.;
// Sweep between the min and max arc.
Angle centerAngle = Angle(hardpoint.GetIdleAngle());
const Angle minArc = hardpoint.GetMinArc();
const Angle maxArc = hardpoint.GetMaxArc();
const double arcMiddleDegrees = (minArc.AbsDegrees() + maxArc.AbsDegrees()) / 2.;
double bias = (centerAngle - hardpoint.GetAngle()).Degrees() / min(arcMiddleDegrees, 180.);
double acceleration = Random::Real() - Random::Real() + bias;
command.SetAim(index, previous + .1 * acceleration);
}
Expand Down Expand Up @@ -3437,7 +3441,32 @@ void AI::AimTurrets(const Ship &ship, FireCommand &command, bool opportunistic)
}

// Determine how much the turret must turn to face that vector.
double degrees = (Angle(p) - aim).Degrees();
double degrees = 0.;
Angle angleToPoint = Angle(p);
if(hardpoint.IsOmnidirectional())
degrees = (angleToPoint - aim).Degrees();
else
{
// For turret with limited arc, determine the turn up to the nearest arc limit.
// Also reduce priority of target if it's not within the firing arc.
const Angle facing = ship.Facing();
1010todd marked this conversation as resolved.
Show resolved Hide resolved
const Angle minArc = hardpoint.GetMinArc() + facing;
const Angle maxArc = hardpoint.GetMaxArc() + facing;
if(!angleToPoint.IsInRange(minArc, maxArc))
{
// Decrease the priority of the target.
rendezvousTime += 2. * weapon->TotalLifetime();

// Point to the nearer edge of the arc.
const double minDegree = (minArc - angleToPoint).Degrees();
const double maxDegree = (maxArc - angleToPoint).Degrees();
if(fabs(minDegree) < fabs(maxDegree))
angleToPoint = minArc;
else
angleToPoint = maxArc;
}
degrees = (angleToPoint - minArc).AbsDegrees() - (aim - minArc).AbsDegrees();
}
double turnTime = fabs(degrees) / weapon->TurretTurn();
// Always prefer targets that you are able to hit.
double score = turnTime + (180. / weapon->TurretTurn()) * rendezvousTime;
Expand Down
21 changes: 21 additions & 0 deletions source/Angle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,14 @@ double Angle::Degrees() const



// Convert an Angle object to degrees, in the range 0 to 360.
double Angle::AbsDegrees() const
{
return angle / DEG_TO_STEP;
}



// Return a point rotated by this angle around (0, 0).
Point Angle::Rotate(const Point &point) const
{
Expand All @@ -183,6 +191,19 @@ Point Angle::Rotate(const Point &point) const



// Judge whether this is inside from "base" to "limit."
// The range from "base" to "limit" is expressed by "clock" orientation.
bool Angle::IsInRange(const Angle& base, const Angle& limit) const
{
// Choose an edge of the arc as the reference angle (base) and
// compare relative angles to decide whether this is in the range.
Angle normalizedLimit = limit - base;
Angle normalizedTarget = *this - base;
return normalizedTarget.angle <= normalizedLimit.angle;
}



// Constructor using Angle's internal representation.
Angle::Angle(const int32_t angle)
: angle(angle)
Expand Down
6 changes: 6 additions & 0 deletions source/Angle.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,16 @@ class Angle {
Point Unit() const;
// Convert an Angle object to degrees, in the range -180 to 180.
double Degrees() const;
// Convert an Angle object to degrees, in the range 0 to 360.
double AbsDegrees() const;

// Return a point rotated by this angle around (0, 0).
Point Rotate(const Point &point) const;

// Judge whether this is inside from "base" to "limit."
// The range from "base" to "limit" is expressed by "clock" orientation.
1010todd marked this conversation as resolved.
Show resolved Hide resolved
bool IsInRange(const Angle &base, const Angle &limit) const;


private:
explicit Angle(int32_t angle);
Expand Down
12 changes: 7 additions & 5 deletions source/Armament.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,19 @@ using namespace std;


// Add a gun hardpoint (fixed-direction weapon).
void Armament::AddGunPort(const Point &point, const Angle &angle, bool isParallel, bool isUnder, const Outfit *outfit)
void Armament::AddGunPort(const Point &point, const Hardpoint::BaseAttributes &attributes,
bool isUnder, const Outfit *outfit)
{
hardpoints.emplace_back(point, angle, false, isParallel, isUnder, outfit);
hardpoints.emplace_back(point, attributes, false, isUnder, outfit);
}



// Add a turret hardpoint (omnidirectional weapon).
void Armament::AddTurret(const Point &point, bool isUnder, const Outfit *outfit)
// Add a turret hardpoint.
void Armament::AddTurret(const Point &point, const Hardpoint::BaseAttributes &attributes,
bool isUnder, const Outfit *outfit)
{
hardpoints.emplace_back(point, Angle(0.), true, false, isUnder, outfit);
hardpoints.emplace_back(point, attributes, true, isUnder, outfit);
}


Expand Down
6 changes: 4 additions & 2 deletions source/Armament.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ class Visual;
class Armament {
public:
// Add a gun or turret hard-point.
void AddGunPort(const Point &point, const Angle &angle, bool isParallel, bool isUnder, const Outfit *outfit = nullptr);
void AddTurret(const Point &point, bool isUnder, const Outfit *outfit = nullptr);
void AddGunPort(const Point &point, const Hardpoint::BaseAttributes &attributes,
bool isUnder, const Outfit *outfit = nullptr);
void AddTurret(const Point &point, const Hardpoint::BaseAttributes &attributes,
bool isUnder, const Outfit *outfit = nullptr);
// This must be called after all the outfit data is loaded. If you add more
// of a given weapon than there are slots for it, the extras will not fire.
// But, the "gun ports" attribute should keep that from happening. To
Expand Down
157 changes: 134 additions & 23 deletions source/Hardpoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@ namespace {


// Constructor.
Hardpoint::Hardpoint(const Point &point, const Angle &baseAngle, bool isTurret,
bool isParallel, bool isUnder, const Outfit *outfit)
: outfit(outfit), point(point * .5), baseAngle(baseAngle), isTurret(isTurret), isParallel(isParallel), isUnder(isUnder)
Hardpoint::Hardpoint(const Point &point, const BaseAttributes &attributes,
bool isTurret, bool isUnder, const Outfit *outfit)
: outfit(outfit), point(point * .5), baseAngle(attributes.baseAngle), baseAttributes(attributes),
isTurret(isTurret), isParallel(baseAttributes.isParallel), isUnder(isUnder)
{
UpdateArc();
}


Expand Down Expand Up @@ -77,14 +79,31 @@ const Angle &Hardpoint::GetAngle() const



// Get the default facing direction for a gun
const Angle &Hardpoint::GetBaseAngle() const
// Get the angle of a turret when idling, relative to the ship.
// For guns, this function is equal to GetAngle().
const Angle &Hardpoint::GetIdleAngle() const
{
return baseAngle;
}



// Get the arc of fire if this is a directional turret,
// otherwise a pair of 180 degree + baseAngle.
const Angle &Hardpoint::GetMinArc() const
{
return minArc;
}



const Angle &Hardpoint::GetMaxArc() const
{
return maxArc;
}



// Get the angle this weapon ought to point at for ideal gun harmonization.
Angle Hardpoint::HarmonizedAngle() const
{
Expand Down Expand Up @@ -122,6 +141,13 @@ bool Hardpoint::IsParallel() const



bool Hardpoint::IsOmnidirectional() const
{
return isOmnidirectional;
}



bool Hardpoint::IsUnder() const
{
return isUnder;
Expand Down Expand Up @@ -201,13 +227,25 @@ void Hardpoint::Step()


// Adjust this weapon's aim by the given amount, relative to its maximum
// "turret turn" rate.
// "turret turn" rate. Up to its angle limit.
void Hardpoint::Aim(double amount)
{
if(!outfit)
return;

angle += outfit->TurretTurn() * amount;
const double add = outfit->TurretTurn() * amount;
if(isOmnidirectional)
angle += add;
else
{
const Angle newAngle = angle + add;
if(add < 0. && minArc.IsInRange(newAngle, angle))
angle = minArc;
else if(add > 0. && maxArc.IsInRange(angle, newAngle))
angle = maxArc;
else
angle += add;
petervdmeer marked this conversation as resolved.
Show resolved Hide resolved
}
}


Expand Down Expand Up @@ -306,20 +344,23 @@ void Hardpoint::Install(const Outfit *outfit)
this->outfit = outfit;
Reload();

// For fixed weapons, apply "gun harmonization," pointing them slightly
// inward so the projectiles will converge. For turrets, start them out
// pointing outward from the center of the ship.
if(!isTurret)
// Update the arc of fire because of changing an outfit.
UpdateArc();

// For fixed weapons and idling turrets, apply "gun harmonization,"
// pointing them slightly inward so the projectiles will converge.
// Weapons that fire parallel beams don't get a harmonized angle.
// And some hardpoints/gunslots are configured not to get harmonized.
// So only harmonize when both the port and the outfit supports it.
if(!isParallel && !outfit->IsParallel())
{
angle = baseAngle;
// Weapons that fire in parallel beams don't get a harmonized angle.
// And some hardpoints/gunslots are configured not to get harmonized.
// So only harmonize when both the port and the outfit supports it.
if(!isParallel && !outfit->IsParallel())
angle += HarmonizedAngle();
const Angle harmonized = baseAngle + HarmonizedAngle();
// The harmonized angle might be out of the arc of a turret.
// If so, this turret is forced "parallel."
if(!isTurret || isOmnidirectional || harmonized.IsInRange(GetMinArc(), GetMaxArc()))
baseAngle = harmonized;
}
else
angle = Angle(point);
angle = baseAngle;
}
}

Expand All @@ -339,6 +380,17 @@ void Hardpoint::Reload()
void Hardpoint::Uninstall()
{
outfit = nullptr;

// Update the arc of fire because of changing an outfit.
UpdateArc();
}



// Get the attributes that can be used as a parameter of the constructor when cloning this.
const Hardpoint::BaseAttributes &Hardpoint::GetBaseAttributes() const
{
return baseAttributes;
}


Expand All @@ -350,20 +402,29 @@ bool Hardpoint::FireSpecialSystem(Ship &ship, const Body &body, std::vector<Visu
// Get the weapon range. Anti-missile and tractor beam shots always last a
// single frame, so their range is equal to their velocity.
double range = outfit->Velocity();
Angle facing = ship.Facing();

// Check if the body is within range of this hardpoint.
Point start = ship.Position() + ship.Facing().Rotate(point);
Point start = ship.Position() + facing.Rotate(point);
Point offset = body.Position() - start;
if(offset.Length() > range)
return false;

// Check if the target is within the arc of fire.
Angle aim(offset);
1010todd marked this conversation as resolved.
Show resolved Hide resolved
if(!isOmnidirectional)
{
const Angle minArc = GetMinArc() + facing;
const Angle maxArc = GetMaxArc() + facing;
if(!aim.IsInRange(minArc, maxArc))
return false;
}

// Precompute the number of visuals that will be added.
visuals.reserve(visuals.size() + outfit->FireEffects().size()
+ outfit->HitEffects().size() + outfit->DieEffects().size());

// Firing effects are displayed at the weapon hardpoint that just fired.
Angle aim(offset);
angle = aim - ship.Facing();
angle = aim - facing;
start += aim.Rotate(outfit->HardpointOffset());
CreateEffects(outfit->FireEffects(), start, ship.Velocity(), aim, visuals);

Expand Down Expand Up @@ -407,3 +468,53 @@ void Hardpoint::Fire(Ship &ship, const Point &start, const Angle &aim)
// case the outfit is its own ammunition.
ship.ExpendAmmo(*outfit);
}



// The arc depends on both the base hardpoint and the installed outfit.
void Hardpoint::UpdateArc()
{
if(!outfit)
return;

// Restore the initial value (from baseAttributes).
isOmnidirectional = baseAttributes.isOmnidirectional;
baseAngle = baseAttributes.baseAngle;
if(isOmnidirectional)
{
const Angle opposite = baseAngle + Angle(180.);
minArc = opposite;
maxArc = opposite;
}
else
{
minArc = baseAttributes.minArc;
maxArc = baseAttributes.maxArc;
}

// The installed weapon restricts the arc of fire.
const double hardpointsArc = (maxArc - minArc).AbsDegrees();
const double weaponsArc = outfit->Arc();
if(weaponsArc < 360. && (isOmnidirectional || weaponsArc < hardpointsArc))
{
isOmnidirectional = false;
const double weaponsHalf = weaponsArc / 2.;

// The base angle is placed as close to center as possible.
const Angle &firstAngle = minArc;
const Angle &secondAngle = maxArc;
double hardpointsMinArc = (baseAngle - firstAngle).AbsDegrees();
double hardpointsMaxArc = (secondAngle - baseAngle).AbsDegrees();
if(hardpointsMinArc < weaponsHalf)
hardpointsMaxArc = weaponsArc - hardpointsMinArc;
else if(hardpointsMaxArc < weaponsHalf)
hardpointsMinArc = weaponsArc - hardpointsMaxArc;
else
{
hardpointsMinArc = weaponsHalf;
hardpointsMaxArc = weaponsHalf;
}
minArc = baseAngle - hardpointsMinArc;
maxArc = baseAngle + hardpointsMaxArc;
}
}
Loading
Loading