NetExalted/XeriarProposal
Contents
Xeriar's NetExalted Structure Proposal
This will be a kind of stripped-down Class diagram for my proposal, modified from my original writeup to make it more class-like and to incorporate some of the requirements posted in the main page. This proposal will cover the guts necessary to enable the following core features:
- Create Heroic Mortals
- Stepwise undoable initial creation and xp spending
- Check for legal combos (important at early stages because it requires more charm data to be entered)
And this optional one:
- Option to automatically purchase prerequisites for a selected Charm, if they have not already been met
Formatting guidelines are given for the following (data structure organization)
- Read every info needed from custom files (no hardcoded data)
- Save and load settings and characters in any stage of the creation process - character file format should be compatible to Anathema
- Ability to import and read Charm files
- The core needs a working and easy to use plugin-interface, of course...
As it's not really my forte (in Java anyway), I'm not going to have a say in the following:
- Display, save as PDF, print character sheets
Right now this is more a list of the most important functions and data structures rather than a full Class heirarchy. Now that I've got more than one of each I can build a more detailed requirements set.
Functional Classes Required
Algorithm Parser
Basic Summary: The Algorithm Parser evaluates equations. This sort of thing is common in more modern languages like Python and PHP, and there is probably Java code out there we can use if it is not yet in the main library.
Requirements:
- Ability to add, subtract, divide, and multiply
- Ability to do if - else style statements
- Ability to evaluate Booleans.
- Ability to handle nested equations.
- Ability to override a basic equation, or (preferably) extend it in such a fashion as to not cause a runaway situation...
- Something is going to have to be worked out to properly calculate situations such as a Dragon-Blooded with Breeding and the Beacon of Power Flaw.
- The simplest solution that comes to mind, since each Essence-weilder has different Essence-equations, to make things like Breeding a part of their base Essence Equations. By current rules this would allow us to limit things to one Equation per Trait, which is handy.
- A plain "None" value should simply return 0.
- Ability to resolve via a specific Trait category. For example, to avoid potential conflicts of having both an Artifact and a Background named Breeding, resolve by referencing in a manner like Background::Breeding or Artifact::Breeding.
The Engine is also going to have to handle some special cases, to resolve statements like:
- This -> MinimumValue
- This -> MaximumDotValue
- This -> MaximumBPValue
- This -> MaximumXPValue
And so on.
Example: Breeding Background
- The Breeding Background adds to the Personal and Peripheral Essence equations for Dragon-Blooded.
- A sample pair of equations to represent this would be:
- PersonalEssence += Breeding
- if (Breeding > 0): if (Breeding == 1): PeripheralEssence += 2; else: PeripheralEssence += 2*Breeding-1
Example 2: Peripheral Essence Trait
- We will use the Solar for the template here, note that the equation sets itself. This should be recognized as a special case for other equations to modify from this root.
- The Solar Peripheral Essence equation would be:
- PeripheralEssence = PermanentEssence*7 + Willpower + Compassion + Conviction + Temperance + Valor
Input: A properly-formatted string of indeterminate length.
Output: A number or an Error value, preferably beginning at -1000 and going down from there, as necessary.
CharacterUpdate() Function
This is mostly a placeholder for now, but, it will need:
- An input value, to search for what things need updating when the user makes a change (We don't want to cycle through everything, just what needs changing).
- This will probably be a recursive function. A Character with Solar Circle Sorcery and a few spells cuts their Essence back to 3 - a lot of changes will be made.
- A memory stack for undo-ness. This will be helped with a recursive function.
Prerequisite Handler
For Charms, mostly. What it does is, within a Trait type, picks out the Prerequisites Property, parses the comma-delimited list, and recurses backwards building up a list of needed Charms.
Data Classes Required
Core Data Structure
The Core Data Structure is the Nirguna / Mana-Yood-Sushai of of the program. It's a simple array of types of Traits and their descriptions. Each element in the array has three parts:
- The name of the Trait: Ability, Armor, Artifact, Attribute, Background, Charm, Combo, Essence, Hearthstone, Shield, Spell, Virtue, Weapon, Willpower
- A list of Properties the Trait has, comma-delimited. For example, Charms would have
- "Group, Cost, Duration, Type, Prerequisites, MinTrait*, Equation"
- An asterisk (*) should signify that more than one may be specified. MinTrait1, MinTrait2, ... and so on.
- As 'Name', 'Description' and 'Book Reference' are assumed, they should not be listed.
- Equation should be a kind of 'key word', signifying that one or more equations are going to be run.
- In the context of Charms, Group refers to Solar, Abyssal, Dragon-Blooded, Sidereal, Lunar, Mountain Folk, Fair Folk, Spirit, Arcanoi, Dragon King Paths, Terrestrial MA, Celestial MA, or Sidereal MA 'Charm Groups'
- Group should be a key word like Equation is. Specific groups will have different costs.
- The description of the Trait: "Hearthstones are pretty rocks"
Merits, Flaws, and the Fair Folk Traits such as Heart and Gifts (and likely Behemoths and such as well) are examples of what further plug-ins may add. This also means that it is quite possible something will end up having two traits (typically, Artifact + something Else), as well as Trait categories being individual Traits themselves (Artifacts are a Background and a Trait category).
Trait Data Structure
The Trait Data Structure uses the Core to get a list of words it's willing to talk to. If Path isn't described, it will throw an error.
Every object in the game that is not a creature, person, or character creation rules set of some sort goes into the Trait Data Structure.
Elements:
- Variable Name: A string to hold the variable name for the Trait - basically it's real name without spaces, to allow for parsing and such. PeripheralEssence, PermanentEssence, MartialArts - lots of WikiWords style writing :-)
- Name: String that gets displayed to the user.
- Book Reference: A string that references the Book and Page of the Trait in question.
- Multiple: A number. 0 if only one of the Trait can be taken (any Charm, multiple levels of Ox-Body and such are referred to by Maximum Value), 1 if multiples can be taken (The basic 'Artifact' Background, buy as many Straight Swords as you want until the Storyteller slaps you, and so on).
- Description: A string, naturally.
- Enumerated String Array: Holds the expected number of properties a given Trait has. This will vary, even within a Trait group - Mountain Folk Charms only have a Minimum Essence property for MinTrait, while Life Extension Prana has a boatload.
The following are not actually drawn from the array the specific Trait has been pulled from, but instead from the Template for the Exalted type (including Heroic Mortal, Dragon King, Ghost etc.) provides. They may, however, be individually modified, so even if these are given a kind of 'default' for faster referencing, this default must be overridable.
- Minimum Value: A string. An equation, but, typically 0 or 1. This needs to be settable (Abyssals setting their Appearance to 0, for example)
- Maximum Value: A string (or rather, several strings). This is often a number (1, for most Charms) but is actually an Equation. Ie:
- if (PermanentEssence > 5): This->MaximumXPValue = PermanentEssence; else This->MaximumXPValue = 5
- There are three Maximum Values that need to be considered. MaximumDotValue, for pre-bonus point creation, MaximumBPValue, for Bonus Points, and MaximumXPValue, for XP.
- XP Cost and BP Cost: Also equations.
Comments
I'm in on the algorithm parser. I'll start on it this weekend. Is making the assumption all traits will start with Capital letters reasonable? Is the semicolon a typo? -- flymolo
- That should be, or we could just keep to reserving keywords. The semicolon ends a statement, the colon begins a conditional statement. That's just my example, however, we might want something a little less C-Java like to help the poor folks doing data input. On the other hand, there are only going to be a few dozen of these in the 'official' plug-in set, so it might not be necessary to make them as simple as possible to use.
- This is still only a proposal, however, and I am working on streamlining it and correcting holes that I am forseeing to the best of my ability :-) -Xeriar
- I started the design this morning and I'm doing my best to make the parser generic as possible. I was going to have user defined functions. so it would be getMinimumValue(Breeding). But I like the properties solution better, but I would suggest Breeding.MinimumValue to avoid too much C/C++ nastiness. I will be implementing most C/C++ operators so you won't have to use +=. = and == being separate makes my life easier, but I'll see. I'll probably have != and <> for not equals. And I'll implement while loops on the first pass.
- How do you plan on undo working should I design with the goal of having Mementos (the list of changes that each equation caused.)? I've never done it before but it seems relatively easy.
- Your breeding example seems like it needs to actually be in the Essence formula, or else someone raising Brreding one dot at a time will be in for a pleasant suprise of extra essence. -flymolo
- Not really sure of anything that needs loops o_O. Though, by this, you would probably end up having the Algorithm parser in the Traits class. That's probably a good idea, actually. Thus any Trait can report its own value, or 0 if it does not exist (obviously, we don't want to store every last Trait on the poor sod's character sheet).
- From a strict standpoint, remembering things isn't going to be the parser's business. When a user drops their Essence from 5 to 3, the CharacterUpdate function will record that change, as well as those that cascade down from it. So it will drop Solar Circle Sorcery, her Solar Circle Spells, then Celestial Circle Sorcery, then her Celestial Circle Spells, preferably in that order, but that's something I'll have to work out in the design and, at this point, it's extranious to us 'getting something working'. The parser is more important to me because it's what lets us do whatever we damn well please, and, having written character generators before, that's an edge when people like RSB actively look your program over to see how they can make your program not cope :-)
- Issues like Breeding are something I'll explain further as I write up the rest of the class, but basically, my design considers two types of equations. When an equation sets itself, that's immutable - the base from which the rest get modified. Any other equations that add or subtract from it get re-parsed when necessary.
- Thinking on it, you will need to return a tuple. The first element would be the string (name of what is getting set) and the second would be its value. I'll update the requirements to fit this all tonight, preferably after more commenting, and make an actual class design. :-) -Xeriar
- I was planning on operating on a dictionary (the interface parent of hashtable) and having the return value be another dictionary. I was planning on throwing exceptions for problems rather than using error codes. I was also planning on having all the possible traits so the Parser could identify problems like trying to give Birth to a Solar. -flymolo
- See my Trait Class writeup. The Parser shoudn't handle that, though it will enable said handling. We can't tie too much into it, otherwise we run the risk of getting stuck down the road. -Xeriar