space-station-14
space-station-14 copied to clipboard
Add guidebook protodata tag: embed prototype values in guidebook text
About the PR
Adds the ability to use the values of component values in entity prototypes in guidebook text.
This is a draft for now because I want to polish it more and possibly expand it a bit. I'm looking for feedback about the implementation and possible improvements that could be made.
Why / Balance
It would be great to list actual numbers for various things in the guidebook (like, how much power does each generator make?), but writers have understandably avoided using them because the guidebook will become out of date the moment someone tweaks the actual values in the prototype YAML.
This feature fixes that problem by allowing guidebook authors to embed references to prototype fields in the text which will automatically stay up to date with any changes in the prototype values.
Technical details
On startup, the server scans all registered components for any fields or prototypes tagged with the GuidebookData attribute. All entity prototypes are then scanned for matching components, and the tagged members are evaluated and their values recorded into a nested dictionary structure.
When a client connects to the server and the clientside GuidebookDataSystem is initialized, it raises a network event at the server to request the recorded values. The server responds by sending all of the recorded values to the client, which stores them for later retrieval. Because it's handy for debugging and it was easy to implement, the server also resends data if entity prototypes are hot reloaded. Outside of these situations, the system makes no further use of networking.
Data can be embedded into the guidebook using a new richtext tag: protodata
. The tag needs to specify the desired data in the format Prototype.Component.Field
. For example [protodata="PortableGeneratorSuperPacman.FuelGenerator.MaxTargetPower"/]
will embed the value of the S.U.P.E.R.P.A.C.M.A.N.'s FuelGeneratorComponent's MaxTargetPower field.
An optional formatting string can be added to the tag, which will be passed to ToString when printing the value, assuming the value's type implements the IFormattable interface (most relevant types do: int, float, double, TimeSpan, etc.). This string should be added to the tag as Prototype.Component.Field:Format
. For example, [protodata="PortableGeneratorSuperPacman.FuelGenerator.MaxTargetPower:N0"/]
will display the S.U.P.E.R.P.A.C.M.A.N.'s FuelGeneratorComponent's MaxTargetPower field as a number with commas every three digits and no decimal places. The usage of format strings in C# is well documented and fairly robust, so have a look at the documentation for float.ToString or whatever to see what can be done here.
If more complicated formatting or fancier calculations are needed, GuidebookDataAttribute
can also be applied to component properties that return derived data using other component members (as long as this stays within the component and doesn't alter state, it's still ECS!).
Media
Portable Generators
FuelGeneratorComponent.cs:
portable_generator.yml:
PortableGenerator.xml:
In-game Guidebook:
Syndicate Bomb
OnUseTimerTriggerComponent.cs:
bombs.yml:
Defusal.xml:
In-game Guidebook:
Note: the guidebook entry already had outdated values here.
- [X] I have added screenshots/videos to this PR showcasing its changes ingame, or this PR does not require an ingame showcase
Breaking changes
None.
Changelog
Nope.
Wow, its so cool
wonder if theres a way we could do custom rendering of certain types, like to embed DamageSpecifier
into guidebooks
or rather--im sure thats possible but wonder how annoying it would be
wonder if theres a way we could do custom rendering of certain types, like to embed
DamageSpecifier
into guidebooks
Hm... well, a custom ToString
on DamageSpecifier
would get used by this. Seems a bit clumsy to use it for this purpose though.
Actually, better idea: an interface for classes that have a method to provide a guidebook format (IGuidebookEmbeddable?) and the client system tries to use that before using ToString as a fallback. That seems like a pretty good solution.
Its a little odd that his doesnt use the serializer at all (which i would have expected from something like this) but the implementation seems broadly okay, any plan to continue on this?
Yeah, I'll come back to this soon.
With all the guidebook changes going on recently, let me know if I should remove the changes to entries from this PR - they're just example uses and can be replaced.
This pull request has conflicts, please resolve those before we can evaluate the pull request.