Several people have asked me why we chose to replace the capture system in 1.31: in an already overdue major release, it seems like we could have spent our time on better things.
The real driver behind replacing the Battleground Europe capture system, without going straight over to an area-capture system, came out of something far more important: changes to the client engine for performance, stability and improving graphics capabilities (which enables more eye candy but was done for performance reasons; with today’s graphics cards, eyecandy features tend to be done on the card, so making use of them helps improve performance).
Jaeger made some radical changes to our terrain engine, which required a large artwork overhaul to comply with. It also presented us with one critical dilemma.
The previous terrain system used marker objects that the player had to come into contact with to initiate capture. These are sometimes referred to as “happy monkey heads”. In pre-alpha the original artwork for them was exactly that :) They later became a table with a radio on it, and finally in 1.30 they became crates.
Here’s the scary thing. They are all manually placed. So for every capture building, there are actually two manual objects: the enclosing building and the table. Ick!
The terrain changes would have required us to manually revisit every single capture table! That’s over 5,000 individual objects. That would be nearly 25 weeks of man work …
Fortunately, the way that a table becomes a capture object turned out to be transferable: they could fairly easily transfer it from the table object to the horizontal surfaces of capture buildings.
Ramp, who was leading the client part of this work, came and discussed this change with me. We looked through the code and estimated the work involved in supporting this change in the host code.
Overhauling the capture code to support this change mean’t a lot of work since it mean’t actively modifying the parts of the strat system I had left in-tact.
However: as we looked, we also saw that this presented an opportunity to discard all of the old code sitting under my layers of new code. It came down to a matter of risk.
In the old code, there are multiple-tiers of data to be massaged and manipulated. The capture object – the table – has to have it’s visual state tied to strat data. When a facility is not capturable, the table is hidden; when the facility is on a timer to becoming capturable, the table is visible with no radio, and finally when the facility is capturable, table and radio appear.
In turn, this adds lots of exception cases to everything that massages object states – which are usually related to damage and destruction.
Also – the previous model applied the capture system on a per-building level. That means that every capture-condition clause was applied to ~53,000 entities rather than 5,000…
I realized an opportunity: we could wholly separate the underlying state system (strat) and the rules. This would make the code much, much simpler, far more readable (maintainable) and expose us to far less risk (the more exceptions “if”s and “else”s in your code, the more complex any testing has to be).
- Move it to per-facility, reducing the overhead and complexity by a factor of 11.
- Given the capture-state it’s own discrete encapsulation (class), which replicated the upper-levels of the old model for easy migration.
- Remove the old client-fired capture events in favor of purely host tracking.
- Replace the client-fired messaging/network overheads with a simpler, clean interface to our Lua engine so that the rules can be abstracted in Lua.
- Create a Lua environment for translating events into capture state changes.
This gives us an awful lot of future proofing. The current “capture system” – by which I mean the C/C++ code – has no idea how stuff gets captured, it just knows what to do when it does get captured :)
All of the rules are implemented in Lua. We sacrifice a little runtime performance for a lot of ease of expression.
It also ensures that the two are implemented discretely. That means the C++ code is no-longer littered with special cases for handling capture, and the capture code is no-longer littered with clauses to ensure we aren’t accidentally looking at objects that we shouldn’t be (I mean, special cases; obviously both systems still have fundamental validation ;-P)
All of which was far easier to get done than it would have been to migrate the old code base to the new system. It would have taken a long time to rewire the old system without tripping mines embedded in special cases where some hard-coded value had special significance to some other system.
So why didn’t we go the whole hog and implement area capture or some other fancier system?
Well, again, see the original reason for doing this work. We got done what needed to be done, and we brought the capture system light years ahead of where it used to be. We also turned it around from a system that was a million years from being area-capture ready to an underlying infrastructure that can support area capture without having to know about it, and a rule system that can implement area capture without needing additional server infrastructure to support it.
We also were able to migrate the capture system from a layer wedged brutally into various other layers, to a discrete system that is well integrated with the client etc, that I think will be alot easier for new players to understand. (I suspect it will confuse established players for a while).