Crafting Skills, and Ranting About UI Programming
Goblinson Crusoe Urho3D hex-based rpg turn-based rpg
Did some more coding on the spellbooks and crafting units UI today. I can see a significant refactor coming pretty soon, now that I have gotten both a better handle on how the UI system works, and have gotten some refinements done on the code I am using to instance UI elements.
Made a few tweaks to the spellcasting system to incorporate crafting skills. The spellcasting system defaulted to a target selection mode when any spell was cast, since most combat skills require a target or target location. However, crafting skills don't take a target and should happen immediately, so I had to make that change.
A crafting skill is essentially just an instant-cast spell. It consumes certain specified resources, and triggers an effect that adds other resources to the caster. In the current tests, both the Basic Workbench and Basic Spell Tome share the same crafting list, with but two elements: Create Wood Planks (consumes 2 Wood, creates 1 Plank) and Create Tinder (Consumes 2 Leaves, creates 1 Tinder). Tinder is now the resource of choice for TestFireball. In order to cast any fireballs, you have to chop down a "tree" (in the caves, mushrooms are trees) in order to obtain a few leaves, then convert those leaves to tinder at a workbench. This is a basic template for most of the gameplay I have in mind: you scavenge around, obtaining materials with which you can cast spells or do other things.
As part of the impending refactor of some of the spellbook UI stuff, I'm going to need to incorporate hover text for skills. Given my small set of icons, it's getting hard to remember which buttons do what since they are all drawn from the same 4 icons.
I've always found UI systems to be awkward. It's just the nature of the beast. You have a hierarchy of elements, all of which require a fairly decent chunk of data to describe, requiring a deeply nested structure of descriptions to be instanced as elements. Either you write a bunch of boilerplate to do it, feeding it a table of data and obtaining an instance hierarchy at the other end, or you build them all by hand, laboriously and with much proneness to error. However, the act of instancing it through boilerplate includes its own gotchas.
For example, UI elements in Urho3D UI generate events that pass through the core event management system, which can be received by any Object-derived class that subscribes to them. You can subscribe to events globally, such as the main-loop Update event. Any object signing up for Update gets the game tick. Or you can subscribe to events originating only from a given object. This is how the nuts and bolts of inter-component communication works. A TakeDamage event sent by a specific Node is listened for in some of the various internal components, to do things like modify life, generate floating combat text, log the hit to the combat log, etc... If these components couldn't sign up to hear Takedamage only from their owning node, then they would receive all TakeDamage events generated anywhere in the game, and the result would be madness.
So in order to get events from a particular UI element, you need to subscribe to events from that specific element. In order to do so, you need a reference to the element. The problem with this approach is that UI hierarchies can get deep. For example, I have a top-level player UI element that contains all of the child elements for a particular player unit, including a container for any spellbooks opened by workbenches, the quick bar, the quick bar spell library, the action bar, and so forth. A spellbook itself is a hierarchy consisting of a background, a scroll view, a close button, a UI element panel holding the buttons/icons representing the spells, etc... It can get deep, and unfortunately these icons/buttons inside the panel are the ones we need to obtain in order to sign up for their events for Pressed, Released, etc...
The problem with instancing a UI through a chunk of boilerplate that consumes a hierarchical description and returns the instanced element, is that all of the child elements are hidden away in a UIElement's children list; and all of their children are hidden away in their own lists. And so on. So to get to the action buttons of a spellbook, I need to descend down through the hierarchy, from the player root window, through the spellbook container, into a particular specified spellbook, into the spellbook's background element, into it's scroll view, into it's button panel and container, and finally to the buttons. Given the clumsiness of the interfaces for delving down through hierarchies, this quickly becomes quite a large pain in the ass.
A solution, of course, is to build all the elements by hand, stashing away references to elements we are interested in using later to sign up for events. This, of course, leads to the even bigger pain in the ass of instancing elements by hand, with all the wordiness and crushing tedium that entails. A compromise is to instance hierarchies for some bits using the boilerplate, and instance the interesting bits by hand, using hierarchical descriptions for those elements in turn and stashing away the references. This is the method I am currently using.
The problem with this method is that it leads to spaghetti code, functions or methods that have to do a lot of by-hand wiring up and munging around. I've already gotten some leaks doing it this way, and I am currently using code that I find to be highly suspicious, and not trustworthy at all. I haven't done any kind of long-duration stress testing on it, and I'm afraid that if/when I do it'll all kind of unravel on me, slowly and messily.
I've always hated UI code, specifically for this hairy mess of getting all the doohickeys wired up to the thingamabobs in anything even approaching an elegant fashion. This tedium is why I find UI programming to be the most onerous part of game development.