Proxy Creatures
Craig Lish added in a branch some code for "Proxy Creatures". Below is what he explained about it.
Don't panic! :o) It won't hurt my feelings if these changes live on
the branch forever, I've just gotten to the point where I wouldn't
want to lose what I've done so made a branch. Remember I mentioned
making an Anything creature type to simplify recruiting? Well, it's
not like I could just stop thinking about it :oP Oh, I started writing
this mail over a week ago, and this top bit has been written at the
end. I'm apprehensive about your reaction, so just know that I'm not
implying you should adopt these changes, I just wanted to show someone
:o)
So, moving from the graph to recruiting subtree (while removing the
statics) removed the natural sort order stored in the graph. The
comparator I wrote couldn't get the warlock after the guardian in
tower recruits :o\ To solve that I reworked custom recruiting again
and created a ProxyCreatureType that implements
Predicate. The proxy is just defined in the terrain and
inserted into the graph like any other creature. It can answer
questions like matches(creature) and getCreatureTypes(hex) to return
the creatures it represents, has a list of creatures (or a pointer to
the main list if anonymous) but doesn't have references to anything
(not heavy), and extends CustomCreatureType. CustomRecruitment only
listens for game events now (to update balrog count, for example), and
has final say on numberOfRecruiterNeeded but will default to the
defined count in the terrain definition. The ProxyCreatureType only
represents other creatures but can have a CustomRecruitment associated
(any creature can).
For example, a BalrogProxyCreatureType represents the 6
BalrogCreatureType instances and is "the right one" when asked for a
specific tower. (Players don't recruit the proxy, it just sits in the
graph to represent a set of creatures.) The BalrogCustomRecruitment is
attached to the BalrogCreaturetype and just updates balrog counts in
response to game events. This way we can say "Anything recruits Balrog
for 0" in the recruit tree instead of calling out each individual
balrog. The 'Anything' matches anything (literally checks a reference
to TerrainRecruitLoader's allCreatureTypes for contains) just like the
AnyNonLordOrDemiLord matches Guardian. Implementing him is when I
discovered the AnyNonLord non-bug :oP (I was playing with logging in
to sourceforge with my openID or whatever, so the bug was reported by
my openID instead of clish -- not the desired effect ;o)
The idea with this change was to move the recruiting information back
into the terrain definition and down into the creatures. There's no
special handling for 'Anything'. Its recruit number is -1 so we know
it can't be recruited, just like Titan. (There's no need for all of
the !recruit.isTitan() checks because he'll never be offered as a
recruit.) There is a RecruiterAndRecruit for Anything/Guardian=3 so
you can ask that question directly. I added
getRecruitsForRecruiter(recruiter, hex) to rst so you can ask for
Anything or AnyNonLordOrDemiLord recruits or whatever (we should
change that to AnyCritter or something). The proxy rar are sorted to
the top so when there's not an exact match we can iterate rars
checking for a match() until we run out of custom creature types.
I made a Titan proxy for the TitanCreatureType instance in each tower
(ala Balrog). The TitanRecruitment updates the correct Titan's power
on score change events. I still need hook the game events better. I
know there's a place, I just haven't found it ;o) I haven't chased all
of the Titan logic through the game, so it will still work without the
proxy and the instances. I was just thinking about it as a use case
for the proxy/recruitment split and just implemented it to see how it
would work.
I made an AcquirableCreatureType that extends CreatureType (not custom
in any way) and has an acquirable value and terrains that is defined
on the creature, not in the terrain definition. The creature loader
deterimines the acquirableRecruitValue that is used for the
calculations. Everything is the same except I moved the data into the
creature. Titan and Balrog creatures extend TowerInstanceCreatureType
that has tower and base_name attributes. A tag will
create one of whatever creatures are defined inside and set tower,
name (base_name + tower), pluralName (base_name + s), and count=1. I
bumped the terrain and creature definition versions.
Oh, I got tired of copying the signature of the CreatureType
constructor around (you have to admit it was getting a bit unwieldy
;o), and that weirdness with the HashMap vs Map parameter is... weird,
so I wrapped the creature element (from the parser) with a
CreatureAttributes class and pass that in the constructor. You can get
and set values on it and it will just add or overwrite the attribute
in the the dom element. Now custom creature types can have special
attributes without changing the constructor signature. That's how I
was able to easily add base_name and acquire_every without changes to
the loader or creature types. I made an enum of creature attribute
names cuz I'm that way :oP
No more special lists or handling for special creature types in rst,
just make a proxy :o) CustomRecruitManager has a reference to the
default proxies (initialized at the end of creature loading, just uses
getCreatureTypeByName and holds the reference). I didn't want to add
Anything, AnyNonLord, etc to all terrain definitions and then trust
that they'll be there at runtime -- the game relies on them and uses
them as code.
Anyway, it's funny because I was just trying to get the guardian in
front of the warlock so the inspector looks right :oP I get that these
are big changes. It worked out really cool and simplified the code a
lot while making it more powerful, but now I have to go into the gui
:o( I haven't tested this, but I'd bet SplitLegion won't split off a
Balrog (or my Titans). The chit only knows the image name of the
creature so that is used to load the creature to split off. Balrog100
has an image name of Balrog. The code is resilient so the creature
that failed to load simply stays with the main group. I just need to
make a CreatureChit that contains the creature, but I'm sure you know
how long that thread is :oP I'd probably refactor all of the creature
based gui to get rid of all the strings, lol.
I have extensive tests on my AI recruitment code that validates
specific recruiting for all hexes in both Default and Abyssal6
variants that have gone back to green without my going in to fix
them. This type of change is extremely risky, but it's localized to
the recruiting sub tree (and the creatures and custom recruiting
and... but it's still just creatures and rst). Just trying to put you
at ease, it's not like I'm just trying to eyeball it.