One of the fun things about doing a game like Clockwork Empires is that you genuinely have no idea what you’re doing some days. Some of our systems haven’t held up through the development process as well as they could, and it’s a constant game of trying to figure out how to extend them and how to refactor them. Case in point: our military AI originally used the same priority system as everything else, but now runs with a decision tree on top of the priority system. Social cues use a proper utility system *below* the priority system, etc., and there is some talk of cleaning the actual jobs up as well to not use the priority system either and to have everything in the game now run on a behavioural tree.
For CE, we chose a behaviour where users would choose what to do. Jobs would get picked up from the job system, we would evaluate them based on weighted values, and then we’d execute them. This is a design decision that got made way back in… 2012, I guess. From there, we started grafting things onto it and ended up with the job framework we currently have. One problem in this scenario that we didn’t account for is “what happens if there are multiple ways a character can accomplish a task?” Case in point: containers. Again.
If I am “returning a commodity”, I can conceivably return it in one of two ways. I can put it in an appropriate container, or I can put it into a stockpile. Both of these objects are objects that can satisfy a need: “I am a thing that can store commodities”; however, a stockpile stores commodities by having you drop commodities in the stockpile, and a container stores commodities by having you drop commodities in a container. Each of these things has a capability that it can satisfy; however, it satisfies it in two different ways.
Pleasantly, the Sims already solved this problem. In the Sims, objects expose capabilities (behaviours they can satisfy), and how they change a Sims’ need (based on utility: a Sim only has six utility functions, and an object broadcasts all the ways it can change a Sim’s utility level. This isn’t quite how our priority system works, but theirs is probably truer to the academic utility function model.) We now support requirements that look for smart objects: a smart object gets sent a bunch of information from the job system to check if it can satisfy a requirement, which calls a function on the object’s script. The object’s script, in turn, determines if it can satisfy the behaviour, and throws stuff over the fence back to the job system: a bunch of parameters for the job system, and an FSM script indicating “how to satisfy this behaviour, with this object.” So now, rather than having one “Return Goods to Stockpile” job, we have “Return Goods”; this will pick between a stockpile and a sensible container, and change the job accordingly.
Containers have turned out to be really tough to get working, but they’re coming along nicely now. A preliminary version of them should be in the next patch.