Consider how to collapse similar rules into a single generic rule.
orendain opened this issue ยท 4 comments
Regarding making the rules more generic: I don't have a strong opinion at the moment. I've been working on the mod mostly in an agile way, with the belief that the right approach for a few different things will become clear as time goes on. IMHO, whatever we get to work, works for me! Especially for the first iteration of the mod.
Rules that require game patching (like multiple zaps), may be hard to generalize as the the code that needs to be patched may be generated differently by the game (i.e., either different methods need to be patched, or the routines to hook into are slightly different per ability, etc.) But rules that don't require an OnPatch
method should indeed be much simpler to make generic.
With regards to when a rule can handle multiple cases:
- For the sake of static typing and efficiency, perhaps a typed structure (instead of JSON) would be beneficial as far as arguments to rule constructors go (e.g., using either straight up primitive types, a
Map<String, String>
, etc.). - Thinking about down the road, if we want users to configure rules via, say, some text file, JSON or some subset of JSON makes sense. RulesAPI could parse the structure and then feed it to the rule's constructor in a more typed fashion (a Map).
- One concern may be input validation and ease of use. For example, it would be easier for users to type
GuardianHealthAdjusted = 20
(and then again for each class), instead of trying to write some JSON/dict. But that's probably easily fixed with simple documentation.- Additionally, in order for RulesAPI to know that the user passed in invalid values (e.g., mispelled
{ Sorcrrer: 20 }
), the rule will have to perform some validation on arbitrary strings and propagate an error which RulesAPI will have to catch and act on. That's not necessarily difficult, but does mean an additional layer of logic to maintain.
- Additionally, in order for RulesAPI to know that the user passed in invalid values (e.g., mispelled
- Just a thought exercise: If in the future, for example, if there were a UI that could expose settings for all rules, it would need well-defined rule parameters in order for it to automatically generate a view with the name of the rule and each rule parameter to tweak.
- For example: It could tell, via reflection, that for
GuardianHealthAdjusted
rule, there is a singleint
parameter labeledhealth
. And it could generate that entry in the UI appropriately. - It would be more difficult for it to know what to generate if the rule took in a JSON or even a typed
Map<String, String>
. One way around this could be using well-defined structs (like classes or what C# callsrecords
) instead of JSON/maps. However, that in turn make instantiating rules slightly more difficult and verbose.
- For example: It could tell, via reflection, that for
Hmm ...
Perhaps a good in-between solution could be for rules to accept only primitive types (i.e., int
, string
, double
, etc.) and only as direct parameters. But with as many parameters as it needs.
- The health adjusted rule could then be implemented with a constructor signature of something like
SomeHealthRuleName(int guardianHealth, int sorcererHealth, ...)
(and could set defaults if need). That way:- We're still very statically typed, allowing RulesAPI core to know what parameters, and their types, are expected, without requiring rules themselves to perform validation.
- The flattened parameter list doubles as documentation (IMHO, easy-to-document code is directly correlated with it's simplicity and maintainability).
- User input via config can still be in some JSON-like format, with the key-value pairs corresponding exactly to the names/types of the parameter list.
My thinking around StartHealth, Movement, VisionRange, ActionPoints is that people might want to weaken or strengthen enemies in addition to the guardian characters. Similarly for the Damage/Heal attributes for the abilties.. it would be nice to be able to be generic in the code to that we don't have to write unique rules for each case.
I was envisaging rulesets along the following lines.
90-min game ruleset:
Health: Guardian +5, Wizard +10, Hunter +5, Asassin +10, Bard +10, Rat King -25, Statue -10, Smite_Ward +30, Torch +30, WolfCompanion +30
ActionPoints: Guardian +1, Wizard +1, Assassin +1, etc
Vision Range: Torch 40, Ballisata 40
0APCost: Zap, Resurrect
FogOffWarOff: Treasure. Goldpile, HealingFountian
HealAmount: Potion: 10, Altar of Healing 20
Battlehardened Ruleset:
Health: RatKing +50, ElvenQueen +30, RootLord +10
Movement: Guardian -1, Wizard -1, Asassin -1 etc
HealAmount: Potion 5, Altar of Healing 5
MeleeDamageAmount: Spiderling 3, Rat 3
Excellent point. Seeing it written out that way really drives home just how many variants of a rule we'd need if they weren't written generically. ๐๐พ
Having raised a #74 for a PiceConfigAdjusted rule, here's a YAML example of how I think rulesets could be defined.
This is my set of tweaks for a rulset called 'It's All About The Support' where the game is pretty much normal at the outset, but when you bring out support pieces on the 2nd or 3rd floor of a dungeon they are significantly more helpful.
---
rules:
- PieceConfigAdjusted:
- WolfCompanion:
- StartHealth: 30
- AttackDamage: 8
- VisionRange: 40
- SwordOfAvalon:
- StartHealth: 30
- ActionPoint: 5
- VisionRange: 40
- BeaconOfSmite:
- StartHealth: 30
- ActionPoint: 3
- VisionRange: 40
- AbilityAdjusted:
- Zap:
- CostAP: False
- Resurrect:
- CostAP: False
- BeaconOfSmite:
- abilityDamage:
- targetDamage: 8
- critDamage: 15
- SwordOfAvalon:
- abilitydDamage:
- targetDamage: 10
- critDamage: 18