TheDreadedAndy / SkyrimAEUncapper-Rust

Rust continuation of the Skyrim AE Uncapper.
10 stars 0 forks source link

INI QoL Suggestions #6

Open cppcooper opened 1 year ago

cppcooper commented 1 year ago

This is more of a feature suggestion than request. Given how flexible uncapper already is, adapting it in a few ways would be absolutely brilliant. And if anything already is a feature, I guess that would fall down to documentation.

[LevelSkillExpMults\CharacterLevel\OneHanded]
# no config, will search next section(s) until one is found
[LevelSkillExpMults\CharacterLevel\TwoHanded]
# define a function for all intervals
funcn=linear-interp
1=10
10=1

[LevelSkillExpMults\CharacterLevel\Enchanting]
# define functions every interval
func1=linear-interp
func2=exponential
# define variables each function is documented having
f1x=3
1=0
40=1
85=4.44

Having some formulas to apply, per interval (potentially) would make a lot of what is done with it simpler through extended syntax. Some basic equations like:

Generalizing as needed, so long as it's documented (perhaps even graphed for documentation purposes). An asymptotic implementation, in Java so not exactly Rust, could look like:

/** Function with asymptotic features.
     *
     * This method makes the following assumptions:
     * 1) input value is in the range (0,1)
     * 2) a return value in the range (0,1) is expected and desired
     * */
    public static double HorizontalAsymptote(double value, double power, boolean positive, boolean increasing){
        if (value > 0) {
            double q = 1 - power;
            double r = q / (value + q);
            r = RemapRange(r, 0, q / (1 + q), 0, 1);

            if (positive && increasing) {
                return 0 - r + 1;
            } else if (positive) {
                return r;
            } else if (increasing) {
                return -r;
            } else {
                return r - 1;
            }
        } else if (value > 1) {
            return 1;
        }
        return 0;
    }

While extending config syntax, might as well apply some DRY by allowing sections to act like a switch. (ie. looking to the next line(s) for config values to use)

// A
switch(x){
case 0:
 // empty code
 break;
case -1:
case -2:
case -3:
  // negative values code
  break;
case 1:
case 2:
case 3:
  // positive values code
  break;
default:
  // invalid value
  break:
}

Edit:

I've been doing a lot of modding of my skyrim lately, and another thought that occurred to me was that it would also be desirable to have global multipliers. This would also contribute to simplifying some setups, or making more nuanced ones.

A global multiplier for the different sections (i.e. [SkillExpGainMults] or [LevelSkillExpMults] granularity into *\BaseSkillLevel and *\CharacterLevel would be optional)

For example maybe the player is using the not a novice mod to start with skills, but doesn't want that to affect their level much. They could achieve that a few ways:

1.

[LevelSkillExpMults/Global]
1 = 0.12
2 = 0.24
3 = 0.48
5 = 1.00
50 = 0.50
  1. if adding global multipliers to subsections
    [LevelSkillExpMults/BaseSkillLevel/Global]
    0 = 0.00
    15 = 0.25
    20 = 0.50
    30 = 1.00

This sort of thing, could also curtail some of the need for DRY features. A benefit, I think, is that this should be backwards compatible. (non-functioning, but non-breaking as well)

TheDreadedAndy commented 1 year ago

I'll consider changes along these lines, but I have some pretty major concerns:

  1. Making any of these changes would ruin backward compatibility with Kassents/Vadfromnus uncapper. It would also wreck the partial backwards compatibility with Elys' LE uncapper. My understanding is that the vast majority of players are still using one of these other implementations, so I'd rather not create a situation where some uncapper files uploaded to nexus only work with my specific implementation (or at least, I'd like to avoid making that situation drastically worse).
  2. While having function syntax would be nice, I think it would ultimately be less flexible then the current 'sane' solution of writing a python script to generate the leveling tiers. That kind of thing could also be done in ERB. In any case, configuration makers/end-users writing a brief script is probably similar in difficulty to learning special not-quite-INI syntax, while also being infinitely more flexible and far less work for me (I'd rather not write a code interpreter, and I think having implicit functions/parameters could be quite confusing for users).
  3. I'd love to have some way to avoid massive repetition in these files, but I'm not sure I like fall-through as a solution. The fact that switch statements fall through in C/C++/Java/etc is, by and large, a very bad thing. I'd rather not recreate that foot-gun with exotic INI syntax. This would also have the problem of causing silently incorrect behavior in the other uncappers (they wouldn't error, they'd just treat any fall-through skills as vanilla). Lastly, this repetition issue is also fairly easy to address with scripting.
  4. I don't generally expect users to have experience with programming, and I feel that the more exotic/programmatic the INI syntax gets, the more confusing this will be for a hypothetical average user.

At the moment, I think it would probably make more sense for someone (not necessarily me lol) to write a graphical tool to generate these sections in the INI file. I believe that would maximize ease of use and compatibility. Such a tool could even leave meta-data in the INI comments so that end-users could update the files themselves.

cppcooper commented 1 year ago
  1. I am dubious about maintaining backwards compatibility while improving QoL of writing the ini configurations. As you bring up, an external tool would be needed to achieve that which would bring up it's own challenges (e.g. consecutive edits knowing the settings used to write the file) and unavoidable consequences (e.g. even longer config files that are even harder to read or edit manually)
  2. I'm unfamiliar with ERB and couldn't find any skyrim tools/mods with that name. That aside, I think even a linear interpolation and step interpolation (i.e. no interpolation) would be very welcome. The whole idea is to allow shortening the configuration file, thus making manual edits faster and easier. Of course allowing interp functions per interval is a bit extreme and very ambitious, but why not ask for the moon if it is in the realm of possibility. As for the "not-quite-ini syntax", I'm sure something could be figured out. Like limiting things to very interpolation functions with no additional variables being needed, and perhaps even using an enum for what function.
  3. Well I can't speak to your preferences, and often a fall-through switch can be replaced. Yet, I don't think I've ever heard them referred to as "very bad" before, I've only seen them as useful. That said, perhaps you'd be more inclined to have custom named sections that can be referenced to indicate where the configuration is located.
  4. I think this is already a problem. In my opinion somebody needs to put together a bit of documentation to ship as a pdf with Uncapper. SkyHUD for example ships a manual for how to configure the ini file. Then a lot of these problems come down to updating the documentation instead of blocking new features for breaking backwards compat.

You're definitely right that a graphical tool to generate sections would be the most compatible end-user solution, but from a development stand point I think adding QoL features to the ini parsing would be the least amount of work by a couple magnitudes. As for ease-of-use, I think both paths are relatively equal with the caveat that reading and manually editing becomes easier in one situation and greatly worse in the other.

TheDreadedAndy commented 1 year ago
  1. It's more that improving QoL of the INI file experience system is not my primary goal with this update. I'm not against doing it, but I'm more concerned with compatibility at the moment as I feel that is more valuable.
  2. ERB = Embedded Ruby. It's a templating language mainly used in web development AFAIK. I've used it with SystemVerilog, C, and Rust when there were no less criminal alternatives.
  3. When I say "fallthrough is bad", I mean that in the situation where the first case does something and then that falls through into a second case that also does something. In my experience, fallthrough is used by that accidentally more than it is intentionally. I have no issues with multiple cases routing to the same place, though.
  4. I may get around to taking care of that, when time permits.

I'm gonna think about it some more. There is merit in making the INI files better, and it is something that's been in the back of my mind. I just am not wanting to accidentally embrace-extend-extinguish the other versions.

If I were to introduce features like this, I definitely would require that it not break the syntax of INI files. In particular, anything where the order of fields in the sections (or sections in the file) matters is automatically out (INI files should logically just be hash maps). I think if I were to implement something which has the features your hoping for, it'd look something like this:

Would those two additions cover the use case your thinking of? As for linear interpolation and an asymptotic function, I'm not sure the former is necessary if a polynomial option exists and I'm not clear on what you meant by the latter. Could you graph it in desmos and send me a link?

In any case, if I decide to implement this it'll likely be some time before it happens (O(months)). I just moved across the country to start work as a software developer, so things are a little hectic right now and I don't have nearly as much free time as I did last month.

cppcooper commented 1 year ago

Asymptotic functions would just be something like: image or image The function I shared just creates a graph like the first one, and manipulates it as needed. That particular function only outputs values 0...1, so it would need to be treated as the 0-1 distance between the start and end values.

I wrote that implementation to allow configuring the asymptote's characteristics. Such as whether the the graph increases to the asymptote or decreases to it, the curve strength, etc. I can try to create an online graphing function to represent it at some point. Many of the various lerp functions would be pretty good options, as well as smoothstep functions.

https://www.febucci.com/2018/08/easing-functions/ https://en.wikipedia.org/wiki/Smoothstep\

edit:

Would those two additions cover the use case your thinking of?

Yea, I think so.

cppcooper commented 1 year ago

edit: moved to OP

TheDreadedAndy commented 1 year ago

Given the way I have the code currently structured, adding global multipliers should be trivial. I'll definitely do that one, I think it could make a lot of peoples lives easier when configuring their files. Thanks for the feedback.

cppcooper commented 11 months ago

Curious if 2.3.0 has some progress

TheDreadedAndy commented 10 months ago

Curious if 2.3.0 has some progress

Not yet. I've been focusing development on bug fixes lately. This is still the next feature I plan on adding, though.