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

FTL Fuel Cost Attributes #5609

Merged
merged 40 commits into from
Oct 23, 2022
Merged

FTL Fuel Cost Attributes #5609

merged 40 commits into from
Oct 23, 2022

Conversation

Ferociousfeind
Copy link
Contributor

@Ferociousfeind Ferociousfeind commented Jan 4, 2021

Feature: This PR aims to allow hyperdrives and jump drives to consume more or less fuel based on two factors, in two ways.

Feature Details

This feature adds five attributes-

  • "drive mass exponent"
  • "drive mass reference"
  • "drive distance exponent"
  • "drive distance reference"
  • "jump startup fuel"

which refer to the ship's mass, and the distance from the ship to its destination system.

The reference attributes are anchors- they specify where changes will not affect fuel cost. Uh, sort of? If your ship has a total mass of 500, and the "drive mass reference" is 250, with a "drive mass exponent" of 4, then the jump your about to make will cost (500/250)^4=2^4=16 times as much as it normally would- likely, 1600 fuel, instead of 100.
Generalized, the mass of your ship, or distance to your destination, is divided by the mass/distance reference, that number is raised to the power of the mass/distance exponent, and that value is multiplied to your fuel consumption. Because the exponent isn't simply a boolean taking either 0 (no effect) or 1 (which could've been a linear or exponential relationship), it can handle more cases than a simple boolean could've. Because the default values for the exponent, if otherwise not specified, is 0, this change should not impact vanilla ES in any way.

The exponent value can be set in any number of ways, including...

  • Exponential: "exponent" > 1, doubling the related attribute more than doubles fuel use
  • Linear: "exponent" = 1, doubling the related attribute doubles fuel use
  • Exponential Decay: 0 < "exponent" < 1, to double the fuel use, you need to more than double the related attribute
  • None: "exponent" = 0, regular, default behavior, mass/distance does not affect fuel use

The attributes are multiplicative against each other, e.g. having a 2x multiplier from mass and a 3x multiplier from distance gives a total (2*3=)6x multiplier on fuel use, instead of (2+3=)5x

Edit:
Jump startup fuel is a secondary "jump fuel" attribute which is unaffected by either the mass of your ship, or the distance to your destination. I suggest you, uhh, don't use it instead of regular jump fuel.

UI Screenshots

N/A

Usage Examples

Here is an example outfit. As explained in the description, doubling the mass of your ship with this equipped will triple fuel costs. At 2000 tons, for example, it will cost 900 fuel per jump. At 4000 tons, 2700 fuel. At 8000 tons, 8100 fuel! Though, I suspect there aren't that many ships weighing 8000 tons.

outfit "Mass Drive"
  category "Systems"
  "mass" 10
  "hyperdrive" 1
  "jump fuel" 100
  "drive mass reference" 500
  "drive mass exponent" 1.585
  description "This hyperdrive is designed to use very little fuel at low ship masses, but as your ship's mass increases, its fuel use increases as well.
  description "Avoid using this drive at high masses- doubling the mass of your ship will triple fuel use per jump, and that will add up quickly."

(at ~7830 tons, this ship will consume ~7830 fuel, by the way)

Using "jump distance" does not interference with "drive distance reference" and "drive distance exponent", and the combination of the four attributes will not interfere with each other- they're built to work together!

Testing Done

I have brought many ships- heavy and light- into space, and had them jump near and far, to observe that the fuel usage is what is expected. I've also used edited drives which lack one of the two pairs of new attributes (however I have not actually tried one half of the pairs without the other half, it is irresponsible to do so anyways). I've selected multiple nearby systems with different distances and observed that the fuel gauge properly updates to reflect fuel consumption if you jumped in that moment.

Edit:
The PR works exactly as intended, both mass and distance work to increase or decrease fuel use at every point that Ship::BestFuel is called, which is pretty much everywhere. If either of the attributes is negative (or zero), they are set to their minimum values (0 for exponents, 0.01 for references) so that they do not catastrophically break the game.

Performance Impact

"Play stupid games, win stupid prizes"- because this adds a long formula into the otherwise-sleek Ship::BestFuel method, if you have hundreds of JDs installed and they all have these four attributes, your game might chug a little as you're pressing J. (Having a large jump range does worse to the game than absurd exponents do anyways.)

will find other files which need editing, don't you worry
added {} to fix things lmao
@Zitchas
Copy link
Member

Zitchas commented Jan 4, 2021

I feel like your explanation of how these variables work needs a graph. If I'm understanding it correctly, this makes fuel costs something like:
y = ax^b
where:
y = fuel cost to jump
x = mass of the ship
a = constant that dictates how fast it ramps up as mass increases
b = constant that dictates whether it is a line or a curve, and if a curve, how steep of a curve.

I think I only vaguely understand what is going on here, but I like the idea.

@Ferociousfeind
Copy link
Contributor Author

Ferociousfeind commented Jan 4, 2021

Yeah, that's pretty much how it works, though be sure to put parentheses around (ax), since the reference applies before the exponent, like this:
image
y= fuel use
f1= specified fuel use in the file
x= [name]
r1= drive [name] reference
e1= drive [name] exponent

With example data, reference of 200 tons, exponent of 0.5, the curve would look like this (kudos to desmos for displaying this)
image
(Notice: fuel use = 100 at the mass reference of 200 tons)

@CaptKrevor
Copy link

Isn't this making the drives (of whatever flavor) nothing more that reaction/thrust drives? Isn't using a hyperlink drive basically "hitching a ride" on the hyperlink between the stars, and thus it's the link that is providing the movement? And a JumpDrive is sort of like creating a miniature, unstable wormhole? Again, the SHIP/DRIVE isn't doing the movement, but rather taking advantage of something else that isn't constrained by the mass of the ship(s). The early spaceships have such reaction/thrust drives that are affected by mass and inertia. But it also meant that Captain Greatgrand Child was the one that landed the ship, no?

@Ferociousfeind
Copy link
Contributor Author

Isn't this making the drives (of whatever flavor) nothing more that reaction/thrust drives? Isn't using a hyperlink drive basically "hitching a ride" on the hyperlink between the stars, and thus it's the link that is providing the movement? And a JumpDrive is sort of like creating a miniature, unstable wormhole? Again, the SHIP/DRIVE isn't doing the movement, but rather taking advantage of something else that isn't constrained by the mass of the ship(s). The early spaceships have such reaction/thrust drives that are affected by mass and inertia. But it also meant that Captain Greatgrand Child was the one that landed the ship, no?

Um... no. First of all, this is an entirely optional change that, as it is, changes nothing about vanilla. It puts another set of four little tools in the content creator's toolbox.

Secondly, the way the hyperdrive and jump drive function isn't well-known. The lore literally states that less than a dozen humans understand how the hyperdrive works. And, it can be assumed that that's a similar situation for the Jump Drive as manufactured by the Korath. (Chances are, none of them know how it works, they're just following the instructions.)

Thirdly, if that is exactly how they work, don't you think a more massive ship will be more demanding on the hyperlink? Wouldn't a larger ship demand more from the same-sized JD to produce a larger wormhole?

I'd love to say your opinion is in the minority, but... that's not true, I've gotten plenty of this exact complaint in the past when I suggested it before.

@CaptKrevor
Copy link

I'd love to say your opinion is in the minority, but... that's not true, I've gotten plenty of this exact complaint in the past when I suggested it before.<

Maybe because a reaction drive that has to push mass requires leaving virtually the same amount of mass behind to move the mass that needs to be moved? Then there's that pesky light speed thing that requires even more push the closer you get to it. Just pointing out that if you want to make things are realistically based on physics as possible, then physics needs to be taken into account as much as possible.

However, IF you want to get into fiction (of the science sort), then get into it. I'm going to go with the theory that hyperdrives and Jumpdrives and other such mechanisms aren't actually pushing anything themselves. They are achieving a phenomenon that is doing it for us. Thus allowing us to gracefully sidestep physics as we know it today. leaves 2 cents behind

@Ferociousfeind
Copy link
Contributor Author

They are achieving a phenomenon that is doing it for us.

See what I already wrote:

Thirdly, if that is exactly how they work, don't you think a more massive ship will be more demanding on the hyperlink? Wouldn't a larger ship demand more from the same-sized JD to produce a larger wormhole?

it is a linear addition to fuel cost which is unaffected by mass nor distance
@CaptKrevor
Copy link

I'm talking about a trigger. The trigger of a .22 caliber pistol is much the same as a trigger of....say....an M60 machine gun. WHAT they do once their trigger is pulled is considerably different. I never had to pull the trigger harder just because the weapon was larger.

In this game, I see the hyperlink, scramdrive and jumpdrives simply being triggers for different modes of travel. All of the drives are of a single size for each of their respective mechanisms.

I could see you pushing for being able to jump FURTHER with more fuel being consumed, but both the hyperlink and scramdrives are dependent upon the existing hyperlinks that already exist between the mapped stars. THAT is not going to change for the most part. That's an established (fictionally) functioning system. There aren't different sized engines that can be installed in different sized ships. Imagine a cruiser sized engine installed in a clipper. :) shrugs

@Ferociousfeind
Copy link
Contributor Author

I don't see the JDs and HDs as triggers, I see them as entire guns. Takes a bigger gun to throw a bigger bullet further.

@MasterOfGrey
Copy link
Member

The fact that the scram drive is larger and uses more fuel does imply that there is some kind of energy scaling involved in the effort of initiating whatever science-fiction it is that allows us to sidestep established physics and disappear into superluminal highways. i.e. It's not just a trigger, or scram would represent a bigger more intensive trigger.
However, they're both independent of mass and so is jump drive, and per MZ's lore dump the warp and weft are related in some astrophysical sense even though they are of course very different (in my head I conceive of them as orthogonal dimensions of reality). This implies that, at the very least, the specific equations for how to escape lightspeed physics are much easier to develop/infer than the generalised equations which might let you exploit specific mass cases for reduced fuel use.

Additionally, having options in the creators toolkit is good, if only so that mods and plugins can pursue stranger and more diversely interesting things.

However, in line with my musings, I would be against applying these to any drives in vanilla unless they:

  1. Are at least T2.
  2. Explain something about an optimal mathematical efficiency that causes mass to cancel out (since hyper and scram are completely independent of mass), but which can be exploited for given cases with the right advanced know-how.

@Ferociousfeind
Copy link
Contributor Author

Fuck the lore, HD now consumes more fuel if your ship is heavier, and JD if you're jumping a further distance

@Amazinite Amazinite added the enhancement A suggestion for new content or functionality that requires code changes label Jan 11, 2021
@Amazinite Amazinite added this to the 0.9.15 milestone Jan 11, 2021
@Ferociousfeind
Copy link
Contributor Author

I'm contemplating locking the values to >= 0, since negative reference definitely will break things, and negative exponent demolishes the pathfinding algorithm. I only actually ever intended them to be all positive anyways. However, the first implementation I imagined (in ship::BestFuel, if the variable is < 0, set it to the default) seems like it'd violate a lot of style choices.

Instead, it feels best to set the variable to the default if it's caught during loading less than 0, and drop a notice in the errors.txt file. Would that be the most proper implementation?

@Ferociousfeind Ferociousfeind deleted the patch-5 branch June 28, 2021 19:50
@Ferociousfeind Ferociousfeind restored the patch-5 branch June 28, 2021 20:23
@Ferociousfeind Ferociousfeind marked this pull request as ready for review June 28, 2021 20:28
Ferociousfeind and others added 5 commits September 3, 2022 00:51
Co-authored-by: Hurleveur <94366726+Hurleveur@users.noreply.github.com>
This may break things- I have not played with lambdas before, and there may be typing issues abound. Forgive me for compile failures.
@Ferociousfeind
Copy link
Contributor Author

The attributes need better names. And ideally, instead of displaying the values directly (they mean nothing to the player), a graph of mass (or distance) versus fuel usage of the drive (perhaps if there are distance parameters it displays a graph of distance versus fuel use, with mass factored in, and otherwise displays the fuel use of your ship's current mass). I don't know how it'd work with multiple ships selected, that's generally an enigma. Probably min - max form, and the graph would be particularly hard to communicate "a range of values" info through.

Ideally, the fuel notches would move down along the fuel gauge as you're engaging hyperspace, to show that one full bar of fuel is being used (down at the bottom) while preserving any partially-filled fuel segment at the top, but I do not think the segmented bar rendering code gives that capability.

If, really, really, only a linear mass -> fuel use relationship is wanted, I could craft a sister PR which does only that- with attribute names like "jump fuel", "jump mass", and "jump penalty" (in fuel units per ton, perhaps normalized to fuel units per 100 tons). It would not be hard to make, given the experience making this one gave me. (But it sure would be demoralizing! 5609, my baby!)

Copy link
Member

@Hurleveur Hurleveur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Syntax wise this looks good to me.

@Ferociousfeind
Copy link
Contributor Author

There, I've done the deed, now it's only lines.

@Hecter94 Hecter94 added waiting on reviewer A Reviewer/Asignee needs to do something, e.g. reviewing, making suggestions, etc. and removed waiting on OP The OP needs to provide something, e.g, making requested changes, posting assets, etc. labels Oct 2, 2022
Copy link
Collaborator

@Amazinite Amazinite left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While looking at this, I've realized that this throws a bit of a wrench in #7378; that PR makes ships cache their fuel costs so that they don't need to be recalculated potentially multiple times a frame as they can be at current. But this PR would make it so that a ship's mass could potentially change its jump cost, invalidating the cached value, meaning I'd need to recalculate a ship's jump cost every time its mass changes. Since a ship's mass includes cargo, that'd mean any time a ship loses cargo (e.g. through dumping it) or gains cargo (e.g. through picking it up), I have to rerun the caching calculation. Is that really something we want to have happen, especially with a feature that I don't see us using in vanilla?

source/Ship.cpp Outdated
Comment on lines 4053 to 4070
auto CalculateFuelCost = [this, mass, jumpDistance](const Outfit &outfit) -> double
{
// If the FTL drive has an attached "jump mass penalty"
// then the base fuel cost is assumed to be at 0 tons.
// Otherwise, base fuel cost is established to be at "jump mass".

// Temporary note: jump mass cost is fuel cost per 100 additional tons.

double fuel = outfit.Get("jump fuel");

// If any of these are not defined, they default to 0, so nothing will break.
fuel += .01 * outfit.Get("jump mass cost") * (mass - outfit.Get("jump base mass"));

// It is possible to generate a negative jump fuel, but I
// do not think this will mess with things in any major way,
// so I will not stop it.
return fuel;
};
Copy link
Collaborator

@Amazinite Amazinite Oct 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fuel cost should not be allowed to go negative. We also need to make sure the jump base mass doesn't push your jump cost to 0, because then it will bounce back up to the default drive cost.

Suggested change
auto CalculateFuelCost = [this, mass, jumpDistance](const Outfit &outfit) -> double
{
// If the FTL drive has an attached "jump mass penalty"
// then the base fuel cost is assumed to be at 0 tons.
// Otherwise, base fuel cost is established to be at "jump mass".
// Temporary note: jump mass cost is fuel cost per 100 additional tons.
double fuel = outfit.Get("jump fuel");
// If any of these are not defined, they default to 0, so nothing will break.
fuel += .01 * outfit.Get("jump mass cost") * (mass - outfit.Get("jump base mass"));
// It is possible to generate a negative jump fuel, but I
// do not think this will mess with things in any major way,
// so I will not stop it.
return fuel;
};
auto CalculateFuelCost = [mass](const Outfit &outfit) -> double
{
double baseCost = outfit.Get("jump fuel");
// Mass cost is the fuel cost per 100 tons of ship mass. The jump base mass of a drive reduces the
// ship's effective mass for the jump mass cost calculation. A ship with a mass below the drive's
// jump base mass is allowed to have a negative cost relative to the base jump fuel cost.
double massCost = .01 * outfit.Get("jump mass cost") * (mass - outfit.Get("jump base mass");
double cost = baseCost + massCost;
// Prevent a drive with a high jump base mass on a ship with a low mass from pushing the total
// cost too low. Put a floor at 1, as a floor of 0 would be forced back up to the default cost, or
// be assumed later on to mean you can't jump.
if(massCost < 0. && cost < 1.)
cost = 1.;
return max(0., cost);
};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would imagine most drives would have 0 mass = some nonzero fuel cost, rather than nonzero mass = zero fuel cost, but if you manage to get to zero fuel cost, I'd say shift the "default the fuel cost" value to -1, not 0, since that is an explicitly disallowed value

Copy link
Collaborator

@Amazinite Amazinite Oct 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would require more changes farther down the line, which I'd rather not dip in to in this PR.

source/Ship.cpp Outdated Show resolved Hide resolved
source/Ship.cpp Outdated Show resolved Hide resolved
source/Ship.cpp Outdated Show resolved Hide resolved
source/Ship.cpp Outdated Show resolved Hide resolved
source/Engine.cpp Outdated Show resolved Hide resolved
data/tooltips.txt Outdated Show resolved Hide resolved
Ferociousfeind and others added 4 commits October 22, 2022 19:14
torn on whether or not `fuelCap` still needs to be defined, or if `flagship->Attributes().Get("fuel capacity")` could simply go into the one place `fuelCap` is called two lines down.
Co-authored-by: Amazinite <jsteck2000@gmail.com>
and clean up changes that shouldn't be there any more

a fresh set of eyes can certainly help
source/Ship.cpp Outdated
// Prevent the cost of jumping from dropping too low.
// Put a floor at 1, as a floor of 0 would be forced back up to the default cost, or
// be assumed later on to mean you can't jump.
return max(1., cost);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way I had it is how it should be. If "jump fuel" is 0 and "jump mass cost" is 0 then this should return 0, not 1.

@Ferociousfeind
Copy link
Contributor Author

I've addressed the immediate concerns, though I can't exactly account for a change that isn't in the game yet... (#7378), so I'll have to at least wait for a decision on that. (Or this gets merged first, and that PR has to deal with the fallout)

Bounce missing base jump costs up to the default before applying the mass cost.
source/Ship.cpp Outdated Show resolved Hide resolved
@Amazinite Amazinite merged commit 0ecb879 into endless-sky:master Oct 23, 2022
@Amazinite Amazinite removed the waiting on reviewer A Reviewer/Asignee needs to do something, e.g. reviewing, making suggestions, etc. label Nov 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement A suggestion for new content or functionality that requires code changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants